├── .gitignore
├── Documentation
├── example_log.csv
├── example_log.txt
├── example_msmtprc
├── example_stresslog.csv
├── history
│ └── CHANGELOG.md
├── rpi_tempmon.cfg
├── screenshots
│ ├── graphmenu.png
│ ├── graphmode1.jpg
│ ├── graphmode10.jpg
│ ├── graphmode11.jpg
│ ├── graphmode12.jpg
│ ├── graphmode2.jpg
│ ├── graphmode3.jpg
│ ├── graphmode4.jpg
│ ├── graphmode5.jpg
│ ├── graphmode6.jpg
│ ├── graphmode7.jpg
│ ├── graphmode8.jpg
│ ├── graphmode9.jpg
│ ├── graphstresstest.jpg
│ ├── main_screen1.jpg
│ └── nyalarm.jpg
└── stresstestdata
│ ├── alldata.jpg
│ ├── stresstest.md
│ ├── test1.jpg
│ ├── test2.jpg
│ ├── test3.jpg
│ ├── test4.jpg
│ ├── test5.jpg
│ ├── test6.jpg
│ └── test7.jpg
├── LICENSE.md
├── README.md
├── rpiTempMod
├── RpiTempmonGraph.py
├── RpiTempmonWork.py
└── __init__.py
├── rpiTempSrc
├── __init__.py
└── rpi_tempmon.py
├── setup.cfg
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | rpiTempSrc/__pycache__
2 | rpiTempMod/__pycache__
3 | rpiTempMod/*.pyc
4 | dist/
5 | build/
6 | rpi_tempmon.py.egg-info/
7 | stuff/
8 | .gout*
--------------------------------------------------------------------------------
/Documentation/example_log.csv:
--------------------------------------------------------------------------------
1 | 18-04-04 09:46:51,61.5,60.7,25.8,33.9,11.6
2 | 18-04-04 09:47:19,60.5,60.1,28.4,33.8,11.6
3 | 18-04-04 09:48:11,64.5,63.4,46.4,43.5,11.6
4 | 18-04-04 09:49:12,67.5,67.7,49.4,51.2,16.9
5 | 18-04-04 09:50:05,66.5,66.6,72.1,36.1,16.9
6 | 18-04-04 09:51:07,62.5,62.3,25.3,16.7,16.9
7 | 18-04-04 09:52:09,60.5,60.1,26.3,14.8,16.9
8 | 18-04-04 09:54:16,59.5,59.1,30.4,16,16.9
9 | 18-04-04 09:55:37,58.5,58.5,30.3,16,16.9
10 |
--------------------------------------------------------------------------------
/Documentation/example_log.txt:
--------------------------------------------------------------------------------
1 | TS = 18-04-20 23:35:15
2 | EP = 1524263715.0
3 | GPU temperature = 50.5'C
4 | CPU temperature = 49.9
5 | Cpu usage = 25.7
6 | RAM usage = 42.3
7 | Swap usage = 58.8
8 | Raspberry pi temperature monitor: raspberrypi
9 | TS = 18-04-20 23:35:21
10 | EP = 1524263722.0
11 | GPU temperature = 50.5'C
12 | CPU temperature = 50.5
13 | Cpu usage = 29.1
14 | RAM usage = 42.0
15 | Swap usage = 58.8
16 | Raspberry pi temperature monitor: raspberrypi
17 | TS = 18-04-20 23:36:10
18 | EP = 1524263771.0
19 | GPU temperature = 50.5'C
20 | CPU temperature = 50.5
21 | Cpu usage = 25.4
22 | RAM usage = 42.3
23 | Swap usage = 58.8
24 | Raspberry pi temperature monitor: raspberrypi
25 | TS = 18-04-20 23:38:27
26 | EP = 1524263907.0
27 | GPU temperature = 50.5'C
28 | CPU temperature = 50.5
29 | Cpu usage = 25.1
30 | RAM usage = 42.7
31 | Swap usage = 58.7
32 | Raspberry pi temperature monitor: raspberrypi
33 | TS = 18-04-20 23:39:02
34 | EP = 1524263943.0
35 | GPU temperature = 50.5'C
36 | CPU temperature = 50.5
37 | Cpu usage = 25.7
38 | RAM usage = 42.7
39 | Swap usage = 58.7
40 | Raspberry pi temperature monitor: raspberrypi
41 | TS = 18-04-21 00:03:38
42 | EP = 1524265419.0
43 | GPU temperature = 55.8'C
44 | CPU temperature = 55.8
45 | Cpu usage = 31.8
46 | RAM usage = 67.0
47 | Swap usage = 66.1
48 | Raspberry pi temperature monitor: raspberrypi
49 | TS = 18-04-21 00:59:54
50 | EP = 1524268794.0
51 | GPU temperature = 53.7'C
52 | CPU temperature = 53.7
53 | Cpu usage = 25.5
54 | RAM usage = 52.7
55 | Swap usage = 86.9
56 | Raspberry pi temperature monitor: raspberrypi
57 | TS = 18-04-21 14:35:29
58 | EP = 1524317730.0
59 | GPU temperature = 58.0'C
60 | CPU temperature = 58.5
61 | Cpu usage = 45.2
62 | RAM usage = 79.9
63 | Swap usage = 75.2
64 | Raspberry pi temperature monitor: raspberrypi
65 | TS = 18-04-21 14:39:51
66 | EP = 1524317992.0
67 | GPU temperature = 56.9'C
68 | CPU temperature = 57.5
69 | Cpu usage = 46.5
70 | RAM usage = 80.5
71 | Swap usage = 90.2
72 | Raspberry pi temperature monitor: raspberrypi
73 |
--------------------------------------------------------------------------------
/Documentation/example_msmtprc:
--------------------------------------------------------------------------------
1 | # Set default values for all following accounts.
2 | defaults
3 | auth on
4 | tls on
5 | tls_trust_file /etc/ssl/certs/ca-certificates.crt
6 | logfile ~/.msmtp.log
7 |
8 | # Gmail
9 | account gmail
10 | host smtp.gmail.com
11 | port 587
12 | from your_gmail_user_name.com
13 | user your_gmail_user_name.com
14 | # Your 16 character API password from google gmail.
15 | password aaaa bbbb cccc dddd
16 |
17 | # A freemail service
18 | account freemail
19 | host smtp.freemail.example
20 | from joe_smith@freemail.example
21 |
22 |
23 | # Set a default account
24 | account default : gmail
25 |
--------------------------------------------------------------------------------
/Documentation/example_stresslog.csv:
--------------------------------------------------------------------------------
1 | 1,52.1,31.3
2 | 2,65.0,100.0
3 | 3,67.7,100.0
4 | 4,69.8,99.9
5 | 5,70.9,99.9
6 |
--------------------------------------------------------------------------------
/Documentation/history/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | Version control history:
2 | ====================
3 |
4 | * Version 1.0-1 020717
5 | * First version
6 |
7 | * Version 1.4-5 290817
8 | * Code rewritten complete in python3 (from a mixture of bash and python)
9 | * Graph of realtime CPU data added
10 |
11 | * Version 1.5-6 220318
12 | * Changes to graph modes due to matplotlib upgrade 2.2.2 causing display issues
13 | * -a data mode and -n notify mode added.
14 |
15 | * Version 2.0-1 040418
16 | * number of graph modes increased to 6 from 2 with menu selection , cli option -G removed
17 | * csv option added -s which converts log file to csv for third party use.
18 | * Added CPU,Ram and swap memory usage data
19 | * mail data now sent as attachment
20 |
21 | * Version 2.1-2 210418
22 | * Epoch unix time added as an alternative to time-date stamp.
23 | * Graph modes increased from 6 to 12
24 | * Stress test added with csv file and graph output.
25 | * Note: old logs files from version 1.x.x will no longer work with Version 2
26 | start again with fresh data.
27 |
28 | * version 2.2-3 210420
29 | * For mail mode, replaced ssmpt with msmtp in mail function.
30 | * ssmtp had to be replaced as apparently ssmtp is deprecated in RPi [Buster](https://raspberrypi.stackexchange.com/questions/99968/cannot-send-mail-from-buster),
31 | * and is considered obsolete, users were reporting issues via mail.
32 | * Users using mail mode option will have to install and configure msmtp.
33 | * Details in Readme.
34 |
35 | * version 2.3-4 05-05-2022
36 | * added new graph mode option 12 GPU & CPU% & RAM% versus live time
37 | * Automated creation of config file if missing (on start up new user)
38 |
39 | * version 2.4-5 12-2023
40 | * Changed setup.py so it installs RPi.GPIO with pipx tool
41 | * fixed GPU data not reporting properly on 64 bit systems
42 |
43 | * version 3.0-1 07-2024
44 | * Switched from Rpi.gpio to gpiozero so it works on raspberry pi 5
45 |
--------------------------------------------------------------------------------
/Documentation/rpi_tempmon.cfg:
--------------------------------------------------------------------------------
1 | [MAIN]
2 | RPI_AuthUser=example@gmail.com
3 | MAIL_ALERT=1
4 | ALARM_MODE=1
5 | CPU_UPPERLIMIT=20
6 | LED_MODE=1
7 | GPIO_LED=26
--------------------------------------------------------------------------------
/Documentation/screenshots/graphmenu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/graphmenu.png
--------------------------------------------------------------------------------
/Documentation/screenshots/graphmode1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/graphmode1.jpg
--------------------------------------------------------------------------------
/Documentation/screenshots/graphmode10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/graphmode10.jpg
--------------------------------------------------------------------------------
/Documentation/screenshots/graphmode11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/graphmode11.jpg
--------------------------------------------------------------------------------
/Documentation/screenshots/graphmode12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/graphmode12.jpg
--------------------------------------------------------------------------------
/Documentation/screenshots/graphmode2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/graphmode2.jpg
--------------------------------------------------------------------------------
/Documentation/screenshots/graphmode3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/graphmode3.jpg
--------------------------------------------------------------------------------
/Documentation/screenshots/graphmode4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/graphmode4.jpg
--------------------------------------------------------------------------------
/Documentation/screenshots/graphmode5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/graphmode5.jpg
--------------------------------------------------------------------------------
/Documentation/screenshots/graphmode6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/graphmode6.jpg
--------------------------------------------------------------------------------
/Documentation/screenshots/graphmode7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/graphmode7.jpg
--------------------------------------------------------------------------------
/Documentation/screenshots/graphmode8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/graphmode8.jpg
--------------------------------------------------------------------------------
/Documentation/screenshots/graphmode9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/graphmode9.jpg
--------------------------------------------------------------------------------
/Documentation/screenshots/graphstresstest.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/graphstresstest.jpg
--------------------------------------------------------------------------------
/Documentation/screenshots/main_screen1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/main_screen1.jpg
--------------------------------------------------------------------------------
/Documentation/screenshots/nyalarm.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/screenshots/nyalarm.jpg
--------------------------------------------------------------------------------
/Documentation/stresstestdata/alldata.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/stresstestdata/alldata.jpg
--------------------------------------------------------------------------------
/Documentation/stresstestdata/stresstest.md:
--------------------------------------------------------------------------------
1 | ### CPU temperature stress test on a RPi using **rpi_tempmon.py** program.
2 |
3 |
4 | Specifications Test 1-6: Raspberry pi 3 model B, running Raspbian 10.0 Buster.
5 | Offical Standard Rpi plastic case from Rpi store.
6 |
7 | Specifications Test 7: Raspberry 5, running Raspbian 12.0 Bookworm.
8 | Offical Standard Rpi plastic case + fan from Rpi store.
9 |
10 | The CPU will be stressed and temperature measurements taken and graphed for
11 | a variety of cooling solutions.
12 | ARM CPU temperature measurements by software **[rpi_tempmon.py](https://github.com/gavinlyonsrepo/raspberrypi_tempmon)** stress test option.
13 | Rpi_tempmon uses the sysbench benchmarking tool to stress CPU.
14 | The CPU stress test consists of calculation of prime numbers up to a value of 20000.
15 | All calculations are performed using 64-bit integers. 4 worker threads are created.
16 | CPU temperature and freq are recorded for each test run and graphed.
17 | 5 test iterations per test. Test 2-6 duration: 10 minutes, Test 1 duration 12 minutes due to
18 | thermal throttling slowing down CPU.
19 |
20 | The first graph is created in libreoffice calc by using CSV(comma separted values)
21 | log file data produced by **rpi_tempmon.py** during testing.
22 | The other graphs are generated by rpi_tempmon.py at end of testrun.
23 |
24 | * Test 1 - RPI3 No Heatsink .
25 | * Test 2 - RPI3 Small Heatsink 13 by 15 by 7 (mm).
26 | * Test 3 - RPI3 Medium Heatsink 16 by 22 by 7 (mm).
27 | * Test 4 - RPI3 Large heatsink 35 by 35 by 22 (mm).
28 | * Test 5 - RPI3 Small Heatsink + 60mm fan running at 5V.
29 | * Test 6 - RPI3 Small Heatsink + 80mm fan running at 9V.
30 | * Test 7 - RPI 5 Official RPI basic Kit case + fan and small heatsink.
31 |
32 | -------------
33 |
34 |
35 | * All Data graph. (test 1-6)
36 |
37 |
38 | 
39 |
40 | ------------
41 |
42 |
43 | * Test 1 - No Heatsink.
44 |
45 | 
46 |
47 | * Test 2 - small Heatsink 13 by 15 by 7 (mm).
48 |
49 | 
50 |
51 | * Test 3 - Small + medium Heatsink 16 by 22 by 7 (mm).
52 |
53 | 
54 |
55 | * Test 4 - Small + medium + large heatsink 35 by 35 by 22 (mm).
56 |
57 | 
58 |
59 | * Test 5 - Small Heatsink + 70mm fan running at 5V.
60 |
61 | 
62 |
63 | * Test 6 - Small heatsink + 80mm fan running at 9V.
64 |
65 | 
66 |
67 | * Test 7 - RPI 5 Official RPI basic Kit case + fan and small heatsink
68 |
69 | 
70 | .
71 |
--------------------------------------------------------------------------------
/Documentation/stresstestdata/test1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/stresstestdata/test1.jpg
--------------------------------------------------------------------------------
/Documentation/stresstestdata/test2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/stresstestdata/test2.jpg
--------------------------------------------------------------------------------
/Documentation/stresstestdata/test3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/stresstestdata/test3.jpg
--------------------------------------------------------------------------------
/Documentation/stresstestdata/test4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/stresstestdata/test4.jpg
--------------------------------------------------------------------------------
/Documentation/stresstestdata/test5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/stresstestdata/test5.jpg
--------------------------------------------------------------------------------
/Documentation/stresstestdata/test6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/stresstestdata/test6.jpg
--------------------------------------------------------------------------------
/Documentation/stresstestdata/test7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/Documentation/stresstestdata/test7.jpg
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 |
2 | GNU GENERAL PUBLIC LICENSE
3 | Version 3, 29 June 2007
4 |
5 | Copyright (C) 2007 Free Software Foundation, Inc.
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The GNU General Public License is a free, copyleft license for
12 | software and other kinds of works.
13 |
14 | The licenses for most software and other practical works are designed
15 | to take away your freedom to share and change the works. By contrast,
16 | the GNU General Public License is intended to guarantee your freedom to
17 | share and change all versions of a program--to make sure it remains free
18 | software for all its users. We, the Free Software Foundation, use the
19 | GNU General Public License for most of our software; it applies also to
20 | any other work released this way by its authors. You can apply it to
21 | your programs, too.
22 |
23 | When we speak of free software, we are referring to freedom, not
24 | price. Our General Public Licenses are designed to make sure that you
25 | have the freedom to distribute copies of free software (and charge for
26 | them if you wish), that you receive source code or can get it if you
27 | want it, that you can change the software or use pieces of it in new
28 | free programs, and that you know you can do these things.
29 |
30 | To protect your rights, we need to prevent others from denying you
31 | these rights or asking you to surrender the rights. Therefore, you have
32 | certain responsibilities if you distribute copies of the software, or if
33 | you modify it: responsibilities to respect the freedom of others.
34 |
35 |
36 | TERMS AND CONDITIONS
37 |
38 | 0. Definitions.
39 |
40 | "This License" refers to version 3 of the GNU General Public License.
41 |
42 | "Copyright" also means copyright-like laws that apply to other kinds of
43 | works, such as semiconductor masks.
44 |
45 | "The Program" refers to any copyrightable work licensed under this
46 | License. Each licensee is addressed as "you". "Licensees" and
47 | "recipients" may be individuals or organizations.
48 |
49 | To "modify" a work means to copy from or adapt all or part of the work
50 | in a fashion requiring copyright permission, other than the making of an
51 | exact copy. The resulting work is called a "modified version" of the
52 | earlier work or a work "based on" the earlier work.
53 |
54 | A "covered work" means either the unmodified Program or a work based
55 | on the Program.
56 |
57 | To "propagate" a work means to do anything with it that, without
58 | permission, would make you directly or secondarily liable for
59 | infringement under applicable copyright law, except executing it on a
60 | computer or modifying a private copy. Propagation includes copying,
61 | distribution (with or without modification), making available to the
62 | public, and in some countries other activities as well.
63 |
64 | To "convey" a work means any kind of propagation that enables other
65 | parties to make or receive copies. Mere interaction with a user through
66 | a computer network, with no transfer of a copy, is not conveying.
67 |
68 | An interactive user interface displays "Appropriate Legal Notices"
69 | to the extent that it includes a convenient and prominently visible
70 | feature that (1) displays an appropriate copyright notice, and (2)
71 | tells the user that there is no warranty for the work (except to the
72 | extent that warranties are provided), that licensees may convey the
73 | work under this License, and how to view a copy of this License. If
74 | the interface presents a list of user commands or options, such as a
75 | menu, a prominent item in the list meets this criterion.
76 |
77 | 1. Source Code.
78 |
79 | The "source code" for a work means the preferred form of the work
80 | for making modifications to it. "Object code" means any non-source
81 | form of a work.
82 |
83 | A "Standard Interface" means an interface that either is an official
84 | standard defined by a recognized standards body, or, in the case of
85 | interfaces specified for a particular programming language, one that
86 | is widely used among developers working in that language.
87 |
88 | The "System Libraries" of an executable work include anything, other
89 | than the work as a whole, that (a) is included in the normal form of
90 | packaging a Major Component, but which is not part of that Major
91 | Component, and (b) serves only to enable use of the work with that
92 | Major Component, or to implement a Standard Interface for which an
93 | implementation is available to the public in source code form. A
94 | "Major Component", in this context, means a major essential component
95 | (kernel, window system, and so on) of the specific operating system
96 | (if any) on which the executable work runs, or a compiler used to
97 | produce the work, or an object code interpreter used to run it.
98 |
99 | The Corresponding Source need not include anything that users
100 | can regenerate automatically from other parts of the Corresponding
101 | Source.
102 |
103 | The Corresponding Source for a work in source code form is that
104 | same work.
105 |
106 | 2. Basic Permissions.
107 |
108 | All rights granted under this License are granted for the term of
109 | copyright on the Program, and are irrevocable provided the stated
110 | conditions are met. This License explicitly affirms your unlimited
111 | permission to run the unmodified Program. The output from running a
112 | covered work is covered by this License only if the output, given its
113 | content, constitutes a covered work. This License acknowledges your
114 | rights of fair use or other equivalent, as provided by copyright law.
115 |
116 | You may make, run and propagate covered works that you do not
117 | convey, without conditions so long as your license otherwise remains
118 | in force. You may convey covered works to others for the sole purpose
119 | of having them make modifications exclusively for you, or provide you
120 | with facilities for running those works, provided that you comply with
121 | the terms of this License in conveying all material for which you do
122 | not control copyright. Those thus making or running the covered works
123 | for you must do so exclusively on your behalf, under your direction
124 | and control, on terms that prohibit them from making any copies of
125 | your copyrighted material outside their relationship with you.
126 |
127 | Conveying under any other circumstances is permitted solely under
128 | the conditions stated below. Sublicensing is not allowed; section 10
129 | makes it unnecessary.
130 |
131 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
132 |
133 | No covered work shall be deemed part of an effective technological
134 | measure under any applicable law fulfilling obligations under article
135 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
136 | similar laws prohibiting or restricting circumvention of such
137 | measures.
138 |
139 | 4. Conveying Verbatim Copies.
140 |
141 | You may convey verbatim copies of the Program's source code as you
142 | receive it, in any medium, provided that you conspicuously and
143 | appropriately publish on each copy an appropriate copyright notice;
144 | keep intact all notices stating that this License and any
145 | non-permissive terms added in accord with section 7 apply to the code;
146 | keep intact all notices of the absence of any warranty; and give all
147 | recipients a copy of this License along with the Program.
148 |
149 | You may charge any price or no price for each copy that you convey,
150 | and you may offer support or warranty protection for a fee.
151 |
152 | 5. Conveying Modified Source Versions.
153 |
154 | You may convey a work based on the Program, or the modifications to
155 | produce it from the Program, in the form of source code under the
156 | terms of section 4, provided that you also meet all of these conditions:
157 |
158 | a) The work must carry prominent notices stating that you modified
159 | it, and giving a relevant date.
160 |
161 | b) The work must carry prominent notices stating that it is
162 | released under this License and any conditions added under section
163 | 7. This requirement modifies the requirement in section 4 to
164 | "keep intact all notices".
165 |
166 | c) You must license the entire work, as a whole, under this
167 | License to anyone who comes into possession of a copy. This
168 | License will therefore apply, along with any applicable section 7
169 | additional terms, to the whole of the work, and all its parts,
170 | regardless of how they are packaged. This License gives no
171 | permission to license the work in any other way, but it does not
172 | invalidate such permission if you have separately received it.
173 |
174 | d) If the work has interactive user interfaces, each must display
175 | Appropriate Legal Notices; however, if the Program has interactive
176 | interfaces that do not display Appropriate Legal Notices, your
177 | work need not make them do so.
178 |
179 | 6. Conveying Non-Source Forms.
180 |
181 | You may convey a covered work in object code form under the terms
182 | of sections 4 and 5, provided that you also convey the
183 | machine-readable Corresponding Source under the terms of this License,
184 | in one of these ways:
185 |
186 | a) Convey the object code in, or embodied in, a physical product
187 | (including a physical distribution medium), accompanied by the
188 | Corresponding Source fixed on a durable physical medium
189 | customarily used for software interchange.
190 |
191 | b) Convey the object code in, or embodied in, a physical product
192 | (including a physical distribution medium), accompanied by a
193 | written offer, valid for at least three years and valid for as
194 | long as you offer spare parts or customer support for that product
195 | model, to give anyone who possesses the object code either (1) a
196 | copy of the Corresponding Source for all the software in the
197 | product that is covered by this License, on a durable physical
198 | medium customarily used for software interchange, for a price no
199 | more than your reasonable cost of physically performing this
200 | conveying of source, or (2) access to copy the
201 | Corresponding Source from a network server at no charge.
202 |
203 | c) Convey individual copies of the object code with a copy of the
204 | written offer to provide the Corresponding Source. This
205 | alternative is allowed only occasionally and noncommercially, and
206 | only if you received the object code with such an offer, in accord
207 | with subsection 6b.
208 |
209 | d) Convey the object code by offering access from a designated
210 | place (gratis or for a charge), and offer equivalent access to the
211 | Corresponding Source in the same way through the same place at no
212 | further charge. You need not require recipients to copy the
213 | Corresponding Source along with the object code. If the place to
214 | copy the object code is a network server, the Corresponding Source
215 | may be on a different server (operated by you or a third party)
216 | that supports equivalent copying facilities, provided you maintain
217 | clear directions next to the object code saying where to find the
218 | Corresponding Source. Regardless of what server hosts the
219 | Corresponding Source, you remain obligated to ensure that it is
220 | available for as long as needed to satisfy these requirements.
221 |
222 | e) Convey the object code using peer-to-peer transmission, provided
223 | you inform other peers where the object code and Corresponding
224 | Source of the work are being offered to the general public at no
225 | charge under subsection 6d.
226 |
227 | A "User Product" is either (1) a "consumer product", which means any
228 | tangible personal property which is normally used for personal, family,
229 | or household purposes, or (2) anything designed or sold for incorporation
230 | into a dwelling. In determining whether a product is a consumer product,
231 | doubtful cases shall be resolved in favor of coverage. For a particular
232 | product received by a particular user, "normally used" refers to a
233 | typical or common use of that class of product, regardless of the status
234 | of the particular user or of the way in which the particular user
235 | actually uses, or expects or is expected to use, the product. A product
236 | is a consumer product regardless of whether the product has substantial
237 | commercial, industrial or non-consumer uses, unless such uses represent
238 | the only significant mode of use of the product.
239 |
240 | "Installation Information" for a User Product means any methods,
241 | procedures, authorization keys, or other information required to install
242 | and execute modified versions of a covered work in that User Product from
243 | a modified version of its Corresponding Source. The information must
244 | suffice to ensure that the continued functioning of the modified object
245 | code is in no case prevented or interfered with solely because
246 | modification has been made.
247 |
248 | If you convey an object code work under this section in, or with, or
249 | specifically for use in, a User Product, and the conveying occurs as
250 | part of a transaction in which the right of possession and use of the
251 | User Product is transferred to the recipient in perpetuity or for a
252 | fixed term (regardless of how the transaction is characterized), the
253 | Corresponding Source conveyed under this section must be accompanied
254 | by the Installation Information. But this requirement does not apply
255 | if neither you nor any third party retains the ability to install
256 | modified object code on the User Product (for example, the work has
257 | been installed in ROM).
258 |
259 | Corresponding Source conveyed, and Installation Information provided,
260 | in accord with this section must be in a format that is publicly
261 | documented (and with an implementation available to the public in
262 | source code form), and must require no special password or key for
263 | unpacking, reading or copying.
264 |
265 | 7. Additional Terms.
266 |
267 | "Additional permissions" are terms that supplement the terms of this
268 | License by making exceptions from one or more of its conditions.
269 | Additional permissions that are applicable to the entire Program shall
270 | be treated as though they were included in this License, to the extent
271 | that they are valid under applicable law. If additional permissions
272 | apply only to part of the Program, that part may be used separately
273 | under those permissions, but the entire Program remains governed by
274 | this License without regard to the additional permissions.
275 |
276 | Notwithstanding any other provision of this License, for material you
277 | add to a covered work, you may (if authorized by the copyright holders of
278 | that material) supplement the terms of this License with terms:
279 |
280 | a) Disclaiming warranty or limiting liability differently from the
281 | terms of sections 15 and 16 of this License; or
282 |
283 | b) Requiring preservation of specified reasonable legal notices or
284 | author attributions in that material or in the Appropriate Legal
285 | Notices displayed by works containing it; or
286 |
287 | c) Prohibiting misrepresentation of the origin of that material, or
288 | requiring that modified versions of such material be marked in
289 | reasonable ways as different from the original version; or
290 |
291 | d) Limiting the use for publicity purposes of names of licensors or
292 | authors of the material; or
293 |
294 | e) Declining to grant rights under trademark law for use of some
295 | trade names, trademarks, or service marks; or
296 |
297 | f) Requiring indemnification of licensors and authors of that
298 | material by anyone who conveys the material (or modified versions of
299 | it) with contractual assumptions of liability to the recipient, for
300 | any liability that these contractual assumptions directly impose on
301 | those licensors and authors.
302 |
303 | All other non-permissive additional terms are considered "further
304 | restrictions" within the meaning of section 10. If the Program as you
305 | received it, or any part of it, contains a notice stating that it is
306 | governed by this License along with a term that is a further
307 | restriction, you may remove that term. If a license document contains
308 | a further restriction but permits relicensing or conveying under this
309 | License, you may add to a covered work material governed by the terms
310 | of that license document, provided that the further restriction does
311 | not survive such relicensing or conveying.
312 |
313 | Additional terms, permissive or non-permissive, may be stated in the
314 | form of a separately written license, or stated as exceptions;
315 | the above requirements apply either way.
316 |
317 | 8. Termination.
318 |
319 | You may not propagate or modify a covered work except as expressly
320 | provided under this License. Any attempt otherwise to propagate or
321 | modify it is void, and will automatically terminate your rights under
322 | this License (including any patent licenses granted under the third
323 | paragraph of section 11).
324 |
325 |
326 | Moreover, your license from a particular copyright holder is
327 | reinstated permanently if the copyright holder notifies you of the
328 | violation by some reasonable means, this is the first time you have
329 | received notice of violation of this License (for any work) from that
330 | copyright holder, and you cure the violation prior to 30 days after
331 | your receipt of the notice.
332 |
333 | Termination of your rights under this section does not terminate the
334 | licenses of parties who have received copies or rights from you under
335 | this License. If your rights have been terminated and not permanently
336 | reinstated, you do not qualify to receive new licenses for the same
337 | material under section 10.
338 |
339 | 9. Acceptance Not Required for Having Copies.
340 |
341 | You are not required to accept this License in order to receive or
342 | run a copy of the Program. Ancillary propagation of a covered work
343 | occurring solely as a consequence of using peer-to-peer transmission
344 | to receive a copy likewise does not require acceptance. However,
345 | nothing other than this License grants you permission to propagate or
346 | modify any covered work. These actions infringe copyright if you do
347 | not accept this License. Therefore, by modifying or propagating a
348 | covered work, you indicate your acceptance of this License to do so.
349 |
350 | 10. Automatic Licensing of Downstream Recipients.
351 |
352 | Each time you convey a covered work, the recipient automatically
353 | receives a license from the original licensors, to run, modify and
354 | propagate that work, subject to this License. You are not responsible
355 | for enforcing compliance by third parties with this License.
356 |
357 | An "entity transaction" is a transaction transferring control of an
358 | organization, or substantially all assets of one, or subdividing an
359 | organization, or merging organizations. If propagation of a covered
360 | work results from an entity transaction, each party to that
361 | transaction who receives a copy of the work also receives whatever
362 | licenses to the work the party's predecessor in interest had or could
363 | give under the previous paragraph, plus a right to possession of the
364 | Corresponding Source of the work from the predecessor in interest, if
365 | the predecessor has it or can get it with reasonable efforts.
366 |
367 | 11. Patents.
368 |
369 | A "contributor" is a copyright holder who authorizes use under this
370 | License of the Program or a work on which the Program is based. The
371 | work thus licensed is called the contributor's "contributor version".
372 |
373 | A contributor's "essential patent claims" are all patent claims
374 | owned or controlled by the contributor, whether already acquired or
375 | hereafter acquired, that would be infringed by some manner, permitted
376 | by this License, of making, using, or selling its contributor version,
377 | but do not include claims that would be infringed only as a
378 | consequence of further modification of the contributor version. For
379 | purposes of this definition, "control" includes the right to grant
380 | patent sublicenses in a manner consistent with the requirements of
381 | this License.
382 |
383 | Each contributor grants you a non-exclusive, worldwide, royalty-free
384 | patent license under the contributor's essential patent claims, to
385 | make, use, sell, offer for sale, import and otherwise run, modify and
386 | propagate the contents of its contributor version.
387 |
388 | In the following three paragraphs, a "patent license" is any express
389 | agreement or commitment, however denominated, not to enforce a patent
390 | (such as an express permission to practice a patent or covenant not to
391 | sue for patent infringement). To "grant" such a patent license to a
392 | party means to make such an agreement or commitment not to enforce a
393 | patent against the party.
394 |
395 | If, pursuant to or in connection with a single transaction or
396 | arrangement, you convey, or propagate by procuring conveyance of, a
397 | covered work, and grant a patent license to some of the parties
398 | receiving the covered work authorizing them to use, propagate, modify
399 | or convey a specific copy of the covered work, then the patent license
400 | you grant is automatically extended to all recipients of the covered
401 | work and works based on it.
402 |
403 | A patent license is "discriminatory" if it does not include within
404 | the scope of its coverage, prohibits the exercise of, or is
405 | conditioned on the non-exercise of one or more of the rights that are
406 | specifically granted under this License. You may not convey a covered
407 | work if you are a party to an arrangement with a third party that is
408 | in the business of distributing software, under which you make payment
409 | to the third party based on the extent of your activity of conveying
410 | the work, and under which the third party grants, to any of the
411 | parties who would receive the covered work from you, a discriminatory
412 | patent license (a) in connection with copies of the covered work
413 | conveyed by you (or copies made from those copies), or (b) primarily
414 | for and in connection with specific products or compilations that
415 | contain the covered work, unless you entered into that arrangement,
416 | or that patent license was granted, prior to 28 March 2007.
417 |
418 | 12. No Surrender of Others' Freedom.
419 |
420 | If conditions are imposed on you (whether by court order, agreement or
421 | otherwise) that contradict the conditions of this License, they do not
422 | excuse you from the conditions of this License. If you cannot convey a
423 | covered work so as to satisfy simultaneously your obligations under this
424 | License and any other pertinent obligations, then as a consequence you may
425 | not convey it at all. For example, if you agree to terms that obligate you
426 | to collect a royalty for further conveying from those to whom you convey
427 | the Program, the only way you could satisfy both those terms and this
428 | License would be to refrain entirely from conveying the Program.
429 |
430 | 13. Use with the GNU Affero General Public License.
431 |
432 | Notwithstanding any other provision of this License, you have
433 | permission to link or combine any covered work with a work licensed
434 | under version 3 of the GNU Affero General Public License into a single
435 | combined work, and to convey the resulting work. The terms of this
436 | License will continue to apply to the part which is the covered work,
437 | but the special requirements of the GNU Affero General Public License,
438 | section 13, concerning interaction through a network will apply to the
439 | combination as such.
440 |
441 | 14. Revised Versions of this License.
442 |
443 | The Free Software Foundation may publish revised and/or new versions of
444 | the GNU General Public License from time to time. Such new versions will
445 | be similar in spirit to the present version, but may differ in detail to
446 | address new problems or concerns.
447 |
448 | Each version is given a distinguishing version number. If the
449 | Program specifies that a certain numbered version of the GNU General
450 | Public License "or any later version" applies to it, you have the
451 | option of following the terms and conditions either of that numbered
452 | version or of any later version published by the Free Software
453 | Foundation. If the Program does not specify a version number of the
454 | GNU General Public License, you may choose any version ever published
455 | by the Free Software Foundation.
456 |
457 | Later license versions may give you additional or different
458 | permissions. However, no additional obligations are imposed on any
459 | author or copyright holder as a result of your choosing to follow a
460 | later version.
461 |
462 | 15. Disclaimer of Warranty.
463 |
464 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
465 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
466 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
467 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
468 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
469 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
470 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
471 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
472 |
473 | 16. Limitation of Liability.
474 |
475 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
476 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
477 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
478 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
479 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
480 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
481 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
482 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
483 | SUCH DAMAGES.
484 |
485 | 17. Interpretation of Sections 15 and 16.
486 |
487 | If the disclaimer of warranty and limitation of liability provided
488 | above cannot be given local legal effect according to their terms,
489 | reviewing courts shall apply local law that most closely approximates
490 | an absolute waiver of all civil liability in connection with the
491 | Program, unless a warranty or assumption of liability accompanies a
492 | copy of the Program in return for a fee.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.paypal.com/paypalme/whitelight976)
2 |
3 | ## Overview
4 |
5 | * Name: rpi_tempmon
6 | * Title : Display the ARM CPU and GPU temperature of Raspberry Pi
7 | * Description:
8 |
9 | This python program will display the ARM CPU and
10 | GPU temperature of a Raspberry Pi
11 | features include command line display, GPIO (LED) output, logging, alarm limit,
12 | graphing, desktop notification, stress tests and e-mailing options.
13 | It is run in terminal and uses matplotlib
14 | plots for graph modes.
15 |
16 | * Author: Gavin Lyons
17 | * URL: https://github.com/gavinlyonsrepo/raspeberrypi_tempmon
18 | * Tested on Toolchains:
19 | 1. RPI 3 model B. Raspbian 10 Buster, 32 bit. Python 3.7.3.
20 | 2. RPI 3 model B. Raspbian 12 Bookworm, 64 bit. Python 3.11.2.
21 | 3. RPI 5 Raspbian 12 Bookworm, 64 bit. Python 3.11.2.
22 |
23 | ## Table of contents
24 |
25 |
26 | * [Overview](#overview)
27 | * [Table of contents](#table-of-contents)
28 | * [Installation](#installation)
29 | * [Usage](#usage)
30 | * [Files and setup](#files-and-setup)
31 | * [Output](#output)
32 | * [Dependencies](#Dependencies)
33 | * [Features](#features)
34 |
35 |
36 | ## Installation
37 |
38 | Latest version 3.0 (07-2024)
39 |
40 | **PyPi & pip , pipx**
41 |
42 | The program is present in python package index, Pypi.
43 | Install using *pip* or *pipx* to the location or environment of your choice.
44 | Package name = rpi-tempmon.py
45 |
46 | **Manually install from github**
47 |
48 | The package is also archived on github and can be manually download and installed
49 | via python and setup.py
50 |
51 | ```sh
52 | curl -sL https://github.com/gavinlyonsrepo/raspberrypi_tempmon/archive/3.0.tar.gz | tar xz
53 | cd raspberrypi_tempmon-3.0
54 | python3 setup.py build
55 | python3 setup.py install --user
56 | ```
57 |
58 | ## Usage
59 |
60 | Program is a python 3 package. Run in a terminal
61 | by typing rpi_tempmon.py or python3 rpi_tempmon.py:
62 |
63 | rpi_tempmon.py -[options][arguments]
64 |
65 | Options list *(Note: Options are stand alone, not designed to be combined)*:
66 |
67 | | Option | Description |
68 | | --------------- | --------------- |
69 | | -h | Print help information and exit |
70 | | -v | Print version information and exit |
71 | | -c | Enters continuous mode, optional number of seconds as a argument eg (-c 5)|
72 | | -l | Creates and/or appends to log file at output folder |
73 | | -L | Creates a sub-folder at output folder with date/time stamp and puts a log file in it |
74 | | -m | Sends the log file to an email account |
75 | | -g | graph mode, Generate a menu where 12 types of graphs can be selected |
76 | | -a | parse log file and produces a data report in terminal |
77 | | -n | send notifications to desktop, Number argument to define behaviour |
78 | | -s | CSV mode , parses log.txt and converts it to log.csv, CSV file |
79 | | -ST | Stress test CPU and measures temperature output to graph and csv file , optional number of test runs as a argument eg (-ST 5)|
80 |
81 | ## Files and setup
82 |
83 | rpi_tempmon files needed are listed below:
84 |
85 | | File Path | Description |
86 | | ------ | ------ |
87 | | rpi_tempmon.py | The main python script |
88 | | RpiTempmonWork.py| python module containing various utility functions used by main |
89 | | RpiTempmonGraph.py | python module dealing with graph output by matplotlib |
90 | | $HOME/.config/rpi_tempmon/rpi_tempmon.cfg | config file |
91 | | README.md | help file |
92 |
93 |
94 | Config file: The config file with dummy values is created if missing.
95 | A dummy config file is available in documentation folder at repository
96 | , used for -m mail option, GPIO/LED feature and the alarm function.
97 |
98 | The setting "RPI_AuthUser" the is email address
99 | destination of data from -m option.
100 |
101 | Mail_Alert(one: mail alert enabled , zero: off)
102 |
103 | The other settings are ALARM_MODE which should be set to one or zero(one: alarm on, zero: off)
104 |
105 | CPU_UPPERLIMIT is the temperature limit of CPU in Centigrade, should be a positive integer.
106 | If alarm mode is on when CPU temperature goes above this limit, the alarm function will activate.
107 |
108 | LED_MODE which should be set to one or zero(one: LED mode on, zero: off) if on
109 | an GPIO pin will switch on during an alarm state in continuous and normal mode.
110 | The RPI GPIO pin as defined by GPIO_LED number. You can connect an LED or another
111 | electronic component to this pin.
112 |
113 | Make sure to include the [MAIN] header and all settings just as below or
114 | copy from the example file.
115 |
116 | Settings:
117 |
118 | [MAIN]
119 | >
120 | >RPI_AuthUser=examplemail@gmail.com
121 | >
122 | >MAIL_ALERT = 1
123 | >
124 | >ALARM_MODE = 1
125 | >
126 | >CPU_UPPERLIMIT = 60
127 | >
128 | >LED_MODE = 0
129 | >
130 | >GPIO_LED = 26
131 | >
132 |
133 | Screenshots, example config/log files are also available in documentation.
134 |
135 | ## Output
136 |
137 | The output folder for log files is currently fixed at:
138 |
139 | ```sh
140 | $HOME/.cache/rpi_tempmon/
141 | ```
142 |
143 | ## Dependencies
144 |
145 |
146 | 1. simple MSMTP - Version: 1.6.6-1- Program which delivers email from a computer to a mailhost.
147 | [MSMTP help](https://wiki.archlinux.org/index.php/Msmtp)
148 | Optional, **ONLY** used by mail functions.
149 | light SMTP client with support for server profiles
150 | client that can be used to send mails .
151 | (SMTP server), needed only for -m mail option.
152 | Also needs to install another few small dependencies (mpack etc) in order to send attachment.
153 | See mail mode in features for setup.
154 |
155 | ```sh
156 | $ sudo apt install msmtp msmtp-mta mailutils mpack
157 |
158 | ```
159 |
160 | 2. sysbench - Version 0.4.12-1.1 - benchmarking tool.
161 | [sysbench](https://manpages.debian.org/testing/sysbench/sysbench.1.en.html)
162 | Optional, **ONLY** used by stress test option -ST.
163 |
164 | ```sh
165 | $ sudo apt install sysbench.
166 | ```
167 |
168 | 3. libnotify-bin - Version 0.7.6-2 - sends desktop notifications to a notification daemon.
169 | [libnotify](http://manpages.ubuntu.com/manpages/artful/man1/notify-send.1.html)
170 | Optional, This is **ONLY** needed if using the -n option which uses the notify-send command.
171 |
172 | ```sh
173 | $ sudo apt install libnotify-bin
174 | ```
175 |
176 | 4. matplotlib - Version: 2.2.2 - Python plotting package.
177 | [matplotlib help](https://matplotlib.org/)
178 | The graph modules requires python module *matplotlib* to draw graphs,
179 | This is for -g and -ST options.
180 | Installed by rpi_tempmon setup.py during installation in theory.
181 |
182 | 5. psutil - Version (2.1.1) - Library for retrieving info on PC.
183 | [psutil](https://psutil.readthedocs.io/en/latest/)
184 | Used to retrieve some CPU and memory information.
185 | Installed by setup.py during installation.
186 |
187 | 6. gpiozero - version(2.0) - This package controls the GPIO on a Raspberry Pi.
188 | [gpiozero](https://github.com/gpiozero/gpiozero)
189 | GPIO Zero is installed by default in the Raspberry Pi OS desktop image, note before rpi_tempmon version 3.0 ,
190 | rpi_tempmon used rpi.gpio.
191 |
192 |
193 | ## Features
194 |
195 | For a raspberry pi the official operating temperature limit is 85°C,
196 | and as a result the Raspberry Pi should start to thermally throttle
197 | performance around 82°C. The GPU and CPU are closely correlated
198 | to within a degree usually.
199 |
200 | The program calculates the ARM CPU and GPU temperature of
201 | a Raspberry Pi and outputs them in Centigrade together with
202 | datetime stamp. Also shows CPU, RAM memory usage .
203 |
204 | The program has ten features
205 | 1. Normal mode - output to screen with optional GPIO output, prompt for update.
206 | 2. Continuous mode - output to screen with optional GPIO output, updates based on timer.
207 | 3. Logfile mode - output to single logfile(also mail mode if mail alert mode on and triggered).
208 | 4. Logfolder mode - output to multiple logfile in separate folders.
209 | 5. Mail mode - output to email.
210 | 6. Graph mode - Displays a graph of logfile created in mode 3
211 | 7. CSV mode - parses log.txt and converts it to log.csv for external use
212 | 8. Data mode - parses log file and produces a small report.
213 | 9. Notify mode - send notifications to desktop, Number argument to define behaviour
214 | 10. Stress test mode - Stresses the CPU with math and records results in csv file and graph.
215 |
216 | **1. normal mode**
217 |
218 | Normal mode is run by running program with no command line arguments.
219 | In normal mode output, Data is sent to the terminal with prompt to repeat or quit.
220 | The GPIO pin in config file will be turned on
221 | and Data in red is displayed in screen for an Alarm state, if setup in config file.
222 |
223 | **2. Continuous mode**
224 |
225 | Same as normal mode except in continuous mode. The program enters a delay between scan.
226 | This delay is set by positive integer argument placed after -c.
227 | For example "-c 30" will wait 30 seconds between scans.
228 | Data is sent to terminal screen.
229 | The GPIO pin in config file will be turned on
230 | and Data in red is displayed in screen for an Alarm state, if setup in config file.
231 |
232 | 
233 |
234 | **3. & 4. Log modes**
235 |
236 | In logfile mode the data is appended into a file log.txt at output folder.
237 | With optional mail setup if alarm mode setup. For mode 3 an email
238 | is sent using mode 5 function,
239 | but with warning in title.
240 |
241 | Sample output of logfile:
242 |
243 | ```sh
244 | TS = 18-04-22 14:19:42
245 | EP = 1524403183.0
246 | GPU temperature = 48.3'C
247 | CPU temperature = 48.3
248 | Cpu usage = 40.4
249 | RAM usage = 45.0
250 | Swap usage = 98.6
251 | Raspberry pi temperature monitor: raspberrypi
252 | ```
253 |
254 | The log file is appended with "Warning:" text message if alarm state entered.
255 |
256 | In logfolder mode in the output folder, a new sub-folder is created each
257 | time it is ran and a new log-file put in here. The sub-folder has following syntax
258 | 1250-02Jul17_RPIT HHMM-DDMMMYY_XXXX.
259 | This folder mode does not work with mail or graph mode at present.
260 |
261 | Logging modes are designed to be used with UNIX automation like crontab.
262 | For example this crontab entry will run logfile mode once an hour,
263 | Note: The path to executable may differ on each users system.
264 |
265 | ```sh
266 | 0 * * * * /usr/local/bin/rpi_tempmon.py -l >/dev/null 2>&1
267 | ```
268 |
269 | **5. Mail mode**
270 |
271 | In mail mode an email is sent using msmtp.
272 | The mail contains the data from logfile mode only, it will NOT work with
273 | sub-folders from logfolder mode.
274 |
275 | msmtp is used rather than than python inbuilt smtplib module
276 | as this program was originally a bash program and this a legacy of that,
277 | also allows access to msmtp config file for greater portability
278 | and security. The program originally used ssmtp but this is now obsolete
279 | in latest raspbian software.
280 |
281 | In order to get mail mode working you must complete 3-4 steps.
282 |
283 | 1. Set settings in rpi_tempmon config file, see Files and setup section.
284 | This file allows for user to set an email address without access to msmtp
285 | config file which should be set up just for root account.
286 | 2. Install msmtp and dependencies as per installation section
287 | 3. Configure msmtp configuration file [MSMTP help](https://wiki.archlinux.org/index.php/Msmtp)
288 | A working example msmtprc config file for gmail is in documentation folder, "example_msmtprc".
289 | 4. Optional, It is also possible you may need to configure your email account to accept msmtp mails
290 | this was the case for gmail and ssmtp. In 2024 in order to get gmail working with ssmtp mail ,you must first set up
291 | 2 step verification on your google account then request a App password for an App from 'apppasswords' section. Google will then generate
292 | a 16 character API password,
293 | like 'aaaa bbbb cccc dddd' , use this in the password field of your msmtp configuration file.
294 |
295 | **6. graph mode**
296 |
297 | In graph mode, the program using matplotlib (plotting library)
298 | creates a plot of various data versus time.
299 | The logfile.txt created by logfile mode 3 is used for data for graph 1-8.
300 | graphs 1-4 use time-date stamp as yaxis value
301 | graphs 5-8 use Unix Epoch stamp as yaxis value, this is better for irregular data
302 | points across multiple dates.
303 | The graphs 9-12 are live plots sampled every two seconds for 150 points,
304 | so five minutes of live data.
305 |
306 | 
307 |
308 | Sample graph screenshot, screenshots of all others are in [screenshot folder of repo](screenshots/).
309 |
310 | 
311 |
312 | 
313 |
314 | **7. CSV(comma-separated values) convert**
315 |
316 | New in Version 2.0. Run with -s on the CLI.
317 | Parses log.txt and creates log.csv.
318 | This csv file can be then used by user in another app.
319 | A comma-separated values (CSV) file is a delimited text file
320 | that uses a comma to separate values. This file can then be loaded into libreoffice calc.
321 | for further processing, for example.
322 |
323 | sample output = time-data, CPU temp, GPU temp, CPU usage , RAM usage , swap usage
324 |
325 | ```sh
326 | 18-04-04 09:46:51,61.5,60.7,25.8,33.9,11.6
327 | ```
328 |
329 | **8. data mode**
330 |
331 | Parses log file created by logfile mode 3 and produces a data report on console.
332 |
333 | **9. notify mode**
334 |
335 | Send notifications to desktop, Numbered argument to define behaviour
336 | After installing notify-send, Additional packages or steps
337 | **may** be required to get notify-send working,
338 | depending on system. for example
339 | [Jessie](https://raspberrypi.stackexchange.com/questions/75299/how-to-get-notify-send-working-on-raspbian-jessie)
340 |
341 |
342 | * -n 2 = argument 2 = If run always display CPU temperature , no warning.
343 | * -n 3 = argument 3 = If run only display if CPU temperature exceeds limit
344 |
345 | 
346 |
347 |
348 | **10. Stress test mode**
349 |
350 | This mode uses the sysbench benchmarking tool.
351 | The test request consists in calculation of prime numbers up to a value of 20000.
352 | All calculations are performed using 64-bit integers. 4 worker threads are created.
353 | The number of test runs is passed on command line as integer max 50 min 2.
354 | CPU temperature and freq are recorded for each test run and are outputed to a csv file,
355 | called stresslog.csv . sample output = test run num, CPU temp, CPU usage.
356 |
357 | ```sh
358 | 1,56.9,27.1
359 | 2,61.3,99.7
360 | ```
361 |
362 | At the end of test, there is an option to display results in a graph.
363 | Stress data carried out by rpi_tempmon can be found in repo [here](Documentation/stresstestdata/stresstest.md)
364 |
--------------------------------------------------------------------------------
/rpiTempMod/RpiTempmonGraph.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | """
3 | Module imported into rpi_tempmon contains
4 | containing a class with methods to display graphs from matlib
5 | Class : MatplotGraph
6 | """
7 | import os
8 | import re
9 | import dateutil
10 | import matplotlib as mpl
11 | mpl.use('tkagg')
12 | import matplotlib.pyplot as plt
13 | import matplotlib.dates as md
14 |
15 | # My modules
16 | from . import RpiTempmonWork as Work
17 | # Local testing, comment out next line# for production
18 | #import RpiTempmonWork as Work
19 |
20 | class MatplotGraph(object):
21 | """
22 | class with 6 methods to display graphs with matplotlib
23 | Methods:
24 | (1) init: pass
25 | (2) graph_log_data: Define data to draw graphs based on user choice
26 | (3) display_menu: Method to display a menu for
27 | user to select graph type
28 | (4) draw_graph: Draw a graph from logdata
29 | (5) graph_live_data: Draw live data graphs
30 | (6) Plot_now: Called from method graph_live_data to draw graph
31 | """
32 |
33 | def __init__(self, name):
34 | """init class with name and define self.selection variable"""
35 | self.name = name
36 | self.selection = 0
37 |
38 | def graph_log_data(self, destlog):
39 | """define data to draw graphs from logdata"""
40 | # display menu return user selection
41 | self.display_menu()
42 |
43 | # get data from log file function data_func
44 | timelist, unixlist, cpulist, gpulist, cpu_uselist, ramlist, swaplist\
45 | = Work.data_func(destlog, True)
46 | if re.match('[1-4]', self.selection):
47 | yaxislist = timelist
48 | elif re.match('[5-8]', self.selection):
49 | yaxislist = unixlist
50 |
51 | if (self.selection == '1' or self.selection == '5'):
52 | plotlabel1 = 'CPU'
53 | plotlabel2 = 'GPU'
54 | ylabel = 'Temperature (degrees)'
55 | title = 'CPU & GPU temperature of RPi'
56 | self.draw_graph(yaxislist, cpulist, gpulist,
57 | plotlabel1, plotlabel2, ylabel, title)
58 |
59 | elif (self.selection == '2' or self.selection == '6'):
60 | plotlabel1 = 'CPU temp'
61 | plotlabel2 = 'CPU usage'
62 | ylabel = 'CPU usage(%) / Temperature (degrees)'
63 | title = 'CPU temperature and usage RPi'
64 | self.draw_graph(yaxislist, cpulist, cpu_uselist,
65 | plotlabel1, plotlabel2, ylabel, title)
66 | elif (self.selection == '3' or self.selection == '7'):
67 | plotlabel1 = 'RAM'
68 | plotlabel2 = 'SWAP'
69 | ylabel = 'Memory (% used)'
70 | title = 'RAM & Swap memory usage of RPi'
71 | self.draw_graph(yaxislist, ramlist, swaplist,
72 | plotlabel1, plotlabel2, ylabel, title)
73 | elif (self.selection == '4' or self.selection == '8'):
74 | plotlabel1 = 'CPU'
75 | ylabel = 'CPU (% used)'
76 | title = 'CPU usage of RPi'
77 | self.draw_graph(yaxislist, cpu_uselist, False,
78 | plotlabel1, False, ylabel, title)
79 | else:
80 | Work.msg_func("red", "Error: graph_log_data: Bad selection value")
81 | return
82 |
83 | def display_menu(self):
84 | """ method to display a menu for
85 | user to select graph"""
86 | os.system('clear')
87 | menu = []
88 | menu.append("CPU and GPU Temperature versus Time-date")
89 | menu.append("CPU Temperature and CPU usage versus Time-date")
90 | menu.append("RAM and Swap memory usage versus Time-date")
91 | menu.append("CPU usage versus Time-date")
92 | menu.append("CPU and GPU Temperature versus Epoch time")
93 | menu.append("CPU Temperature and CPU usage versus Epoch time")
94 | menu.append("RAM and Swap memory usage versus Epoch time")
95 | menu.append("CPU usage versus Epoch time")
96 | menu.append("CPU usage versus live Time")
97 | menu.append("GPU Temperature versus live Time")
98 | menu.append("RAM usage versus live Time")
99 | menu.append("CPU GPU & RAM usage versus live time")
100 | menu.append("Exit")
101 | try:
102 | while True:
103 | print("\n")
104 | Work.msg_func("blue", "RPI_tempmon :: Graph Menu Options")
105 | Work.msg_func("line", "")
106 | for number, string in enumerate(menu):
107 | print(number+1, string)
108 | Work.msg_func("line", "")
109 | self.selection = (input("Please Select:"))
110 | if int(self.selection) <= 8:
111 | return
112 | elif self.selection == '9':
113 | self.graph_live_data("CPU")
114 | break
115 | elif self.selection == '10':
116 | self.graph_live_data("GPU")
117 | break
118 | elif self.selection == '11':
119 | self.graph_live_data("RAM")
120 | break
121 | elif self.selection == '12':
122 | self.graph_all_live_data()
123 | break
124 | elif self.selection == '13':
125 | quit()
126 | else:
127 | Work.msg_func("red", "\n ** Warning : Unknown Option Selected! **")
128 | Work.msg_func("anykey", "")
129 | os.system('clear')
130 | except ValueError as error:
131 | print(error)
132 | Work.msg_func("red", "Error: Wrong menu Input: Integer only : Try Again")
133 | quit()
134 |
135 | def draw_graph(self, timelist, yaxis_list1,
136 | yaxis_list2, plot_label1, plot_label2, yaxis_label, graph_title):
137 | """ Method to draw graphs two modes, single and doulbe yaxis """
138 | # convert to ints as strings cause issue with graph in new matlib version
139 | yaxis_list1 = list(map(float, yaxis_list1))
140 |
141 | if plot_label2: # single plot graph mode
142 | yaxis_list2 = list(map(float, yaxis_list2))
143 |
144 | plt.xticks(rotation=90)
145 | plt.xticks(fontsize=6)
146 | plt.subplots_adjust(bottom=0.2)
147 | axisx = plt.gca()
148 | # check user input for time date or unix epoch for yaxis
149 | if self.selection == 0:
150 | mydates = timelist
151 | plt.xlabel('TestRuns')
152 | elif re.match('[1-4]', self.selection):
153 | mydates = [dateutil.parser.parse(s) for s in timelist]
154 | axisx.set_xticks(mydates)
155 | xfmt = md.DateFormatter('%m/%d %H:%M')
156 | axisx.xaxis.set_major_formatter(xfmt)
157 | plt.xlabel('Date time stamp (DD-MM HH:MM)')
158 | elif re.match('[5-8]', self.selection):
159 | mydates = timelist
160 | plt.xlabel('Unix epoch time (seconds)')
161 |
162 | axisx.xaxis.label.set_color('red')
163 | axisx.yaxis.label.set_color('red')
164 |
165 | plt.plot(mydates, yaxis_list1,
166 | label=plot_label1, color='green', marker='x')
167 | if plot_label2: # single plot graph mode
168 | plt.plot(mydates,
169 | yaxis_list2, label=plot_label2, marker='*')
170 |
171 | plt.ylabel(yaxis_label)
172 | plt.title(graph_title, color='green')
173 | plt.legend(loc='upper right', fancybox=True, shadow=True)
174 | plt.grid(True)
175 | plt.show()
176 |
177 | def graph_all_live_data(self):
178 | """ Draw a live graph of pi GPU/CPU/RAM """
179 | print("Drawing graph of all data usage versus live time")
180 | print("Press CTRL+c to quit.")
181 | try:
182 | time_cpu_axis = []
183 | time_ram_axis = []
184 | time_gpu_axis = []
185 | yaxis_cpu_data = 0
186 | yaxis_ram_data = 0
187 | yaxis_gpu_data = 0
188 | plt.ion()
189 | labels = ()
190 |
191 | # pre-load dummy data
192 | for i in range(0, 150):
193 | time_cpu_axis.append(.5)
194 | time_ram_axis.append(.5)
195 | time_gpu_axis.append(.5)
196 |
197 | while True:
198 | # get data
199 | yaxis_cpu_data = Work.get_cpu_use()
200 | yaxis_cpu_data = float(yaxis_cpu_data)
201 | yaxis_ram_data = Work.get_ram_info()
202 | yaxis_ram_data = float(yaxis_ram_data)
203 | ostemp = os.popen('vcgencmd measure_temp').readline()
204 | yaxis_gpu_data = (ostemp.replace("temp=", "").replace("'C\n", ""))
205 | yaxis_gpu_data = float(yaxis_gpu_data)
206 |
207 | # update the graph
208 | labels = "GPU Temp + CPU & RAM usage", "CPU-% RAM-% GPU-'C", "CPU-%", "RAM-%", "GPU-'C"
209 | time_cpu_axis.append(yaxis_cpu_data)
210 | time_ram_axis.append(yaxis_ram_data)
211 | time_gpu_axis.append(yaxis_gpu_data)
212 | time_cpu_axis.pop(0)
213 | time_ram_axis.pop(0)
214 | time_gpu_axis.pop(0)
215 | self.plot_all_now(time_cpu_axis, time_ram_axis, time_gpu_axis, labels)
216 | plt.pause(2)
217 |
218 | except Exception as error:
219 | print(error)
220 | Work.msg_func("bold", "Real-time matplotlib plot shutdown")
221 | quit()
222 |
223 | def plot_all_now(self, time_cpu_axis, time_ram_axis, time_gpu_axis, labels):
224 | """ Called from method graph_all_live_data to draw graph"""
225 |
226 | title, y_label, plot_cpu_label, plot_ram_label, plot_gpu_label = labels
227 | plt.clf()
228 |
229 | plt.ylim([1, 100])
230 | plt.ylabel(y_label, color='red')
231 |
232 | plt.title(self.name + title, color='green')
233 | plt.grid(True)
234 | plt.xlabel("Time (last 300 seconds)", color='red')
235 | plt.plot(time_cpu_axis, color='blue', marker='', label=plot_cpu_label)
236 | plt.plot(time_ram_axis, color='red', marker='', label=plot_ram_label)
237 | plt.plot(time_gpu_axis, color='green', marker='', label=plot_gpu_label)
238 | plt.legend(loc='upper right', fancybox=True, shadow=True)
239 | plt.show()
240 |
241 | def graph_live_data(self, choice):
242 | """ Draw a live graph of pi GPU or CPU or RAM """
243 | try:
244 | time_axis = []
245 | yaxis_data = 0
246 | plt.ion()
247 | labels = ()
248 | # pre-load dummy data
249 | for i in range(0, 150):
250 | time_axis.append(.5)
251 |
252 | while True:
253 | if choice == "GPU":
254 | ostemp = os.popen('vcgencmd measure_temp').readline()
255 | yaxis_data = (ostemp.replace("temp=", "").replace("'C\n", ""))
256 | labels = (" GPU live temp", "Temperature ('C)", "GPU")
257 | yaxis_data = float(yaxis_data)
258 | time_axis.append(yaxis_data)
259 | time_axis.pop(0)
260 | self.plot_now(time_axis, labels)
261 | elif choice == 'CPU':
262 | yaxis_data = Work.get_cpu_use()
263 | yaxis_data = float(yaxis_data)
264 | labels = (" CPU live usage", "Usage (%)", "CPU")
265 | time_axis.append(yaxis_data)
266 | time_axis.pop(0)
267 | self.plot_now(time_axis, labels)
268 | elif choice == 'RAM':
269 | yaxis_data = Work.get_ram_info()
270 | yaxis_data = float(yaxis_data)
271 | labels = (" RAM live usage", "Usage (%)", "RAM")
272 | time_axis.append(yaxis_data)
273 | time_axis.pop(0)
274 | self.plot_now(time_axis, labels)
275 |
276 | plt.pause(2)
277 | except Exception as error:
278 | print(error)
279 | Work.msg_func("bold", "Real-time matplotlib plot shutdown")
280 | quit()
281 |
282 | def plot_now(self, timeaxis, labels):
283 | """ Called from method graph_live_data to draw graph"""
284 |
285 | title, y_label, plot_label = labels
286 | plt.clf()
287 |
288 | plt.ylim([1, 100])
289 | plt.ylabel(y_label, color='red')
290 |
291 | plt.title(self.name + title, color='green')
292 | plt.grid(True)
293 | plt.xlabel("Time (last 300 seconds)", color='red')
294 | plt.plot(timeaxis, color='blue', marker='*', label=plot_label)
295 | plt.legend(loc='upper right', fancybox=True, shadow=True)
296 | plt.show()
297 |
298 |
299 | def importtest(text):
300 | """import print test statement"""
301 | # print(text)
302 | pass
303 |
304 | # ===================== MAIN ===============================
305 |
306 |
307 | if __name__ == '__main__':
308 | importtest("main")
309 | else:
310 | importtest("Imported {}".format(__name__))
311 |
312 | # ===================== END ===============================
313 |
--------------------------------------------------------------------------------
/rpiTempMod/RpiTempmonWork.py:
--------------------------------------------------------------------------------
1 |
2 | """
3 | Module imported into rpi_tempmon contains various utility functions
4 | Functions in this module
5 | (1) csv convert : converts log.txt into log.csv
6 | (2) notify_func : function to control notify-send alerts
7 | (3) get_cpu_tempfunc: Return CPU temperature
8 | (4) get_gpu_tempfunc: Return GPU temperature as a character string
9 | (5) get_cpu_use: Return CPU usage using psutil
10 | (6) get_ram_info: Return RAM usage using psutil
11 | (7) get_swap_info: Return swap memory usage using psutil
12 | (8) logging_func: sents log data to file
13 | (9) mail_func: Sends mail via msmpt
14 | (10) data_func: Function to parse log file also produce a Data report
15 | (11) stress_func: Carry out stress test
16 | (12) msg_func: Writes to console
17 | """
18 | import os
19 | import datetime
20 | import csv
21 | import time
22 | import psutil
23 |
24 | def csv_convert(destlog):
25 | """ Function to convert log.txt to log.csv"""
26 | try:
27 | os.chdir(destlog)
28 | # Read in log.txt file
29 | cputemp = ""
30 | time_stamp = ""
31 | gputemp = ""
32 | cpu = ""
33 | ram = ""
34 | swap = ""
35 |
36 | # csv file
37 | mycsvpath = destlog + "/" + "log.csv"
38 | output_file = open(mycsvpath, 'w', newline='')
39 | output_writer = csv.writer(output_file)
40 |
41 | # get data from file and put into csv file
42 | mylogpath = destlog + "/" + "log.txt"
43 | if os.path.isfile(mylogpath):
44 | with open(mylogpath, 'r') as myfile:
45 | for line in myfile:
46 | if "TS" in line:
47 | time_stamp = line[5:-1]
48 | if "CPU" in line:
49 | cputemp = line[18:22]
50 | if "GPU" in line:
51 | gputemp = line[18:22]
52 | if "Cpu" in line:
53 | cpu = line[12:16]
54 | if "RAM" in line:
55 | ram = line[12:16]
56 | if "Swap" in line:
57 | swap = line[13:17]
58 | if "Raspberry" in line:
59 | output_writer.writerow([time_stamp, cputemp, gputemp, cpu, ram, swap])
60 | output_file.close()
61 | else:
62 | msg_func("red", "Error: Log file not found at {}".format(mylogpath))
63 |
64 | except Exception as error:
65 | msg_func("red", "Problem with csv convert function {}".format(error))
66 | msg_func("red", "Check that log file exists, is not corrupt")
67 | msg_func("red", "or created by a version pre-2.0-1 - {}".format(mylogpath))
68 | else:
69 | msg_func("bold", "Log File conversion from text to csv completed")
70 |
71 |
72 | def notify_func(cli_arg_2, cpu_limit):
73 | """ function to control notify-send alerts, passed in second command line argument"""
74 | title = "rpi_tempmon"
75 | message = ""
76 |
77 | if cli_arg_2 == "2":
78 | # send notify always
79 | message = "CPU temperature => " + get_cpu_tempfunc() + "'C"
80 | os.system('notify-send "{}" "{}"'.format(title, message))
81 | elif cli_arg_2 == "3":
82 | # send notify if cpulimit exceed)
83 | if get_cpu_tempfunc() > cpu_limit:
84 | message = "ALARM : CPU temp => {}'C : Limit at {}'C"\
85 | .format(get_cpu_tempfunc(), cpu_limit)
86 | os.system('notify-send "{}" "{}"'.format(title, message))
87 | else:
88 | msg_func("red", "Error invalid argument to -n option 2 or 3 only")
89 |
90 |
91 | def get_cpu_tempfunc():
92 | """ Return CPU temperature """
93 | result = 0
94 | mypath = "/sys/class/thermal/thermal_zone0/temp"
95 | with open(mypath, 'r') as mytmpfile:
96 | for line in mytmpfile:
97 | result = line
98 |
99 | result = float(result)/1000
100 | result = round(result, 1)
101 | return str(result)
102 |
103 |
104 | def get_gpu_tempfunc():
105 | """ Return GPU temperature as a character string"""
106 | res = os.popen('vcgencmd measure_temp').readline()
107 | return (res.replace("\n", "").replace("temp=", ""))
108 |
109 |
110 | def get_cpu_use():
111 | """ Return CPU usage using psutil"""
112 | cpu_cent = psutil.cpu_percent()
113 | return str(cpu_cent)
114 |
115 |
116 | def get_ram_info():
117 | """ Return RAM usage using psutil """
118 | ram_cent = psutil.virtual_memory()[2]
119 | return str(ram_cent)
120 |
121 |
122 | def get_swap_info():
123 | """ Return swap memory usage using psutil """
124 | swap_cent = psutil.swap_memory()[3]
125 | return str(swap_cent)
126 |
127 |
128 | def logging_func(choice, mail_alert, cpu_limit, mailuser, destlog, alarm_mode):
129 | """ Function to log temps data to file,
130 | passed choice(args parameter which called it
131 | and 3 options from config file and path to log file"""
132 | try:
133 | if choice == "l":
134 | os.chdir(destlog)
135 | else:
136 | # Makes a directory with time/date stamp and enters it
137 | os.chdir(destlog)
138 | dirvar = datetime.datetime.now().strftime("%H:%M:%S%a%b%d")
139 | dirvar = destlog + "/" + dirvar + "_RPIT"
140 | if not os.path.exists(dirvar):
141 | os.makedirs(dirvar)
142 | os.chdir(dirvar)
143 |
144 | # making time string
145 | today = datetime.datetime.now().strftime("%y-%m-%d %H:%M:%S")
146 | # write to file
147 | with open("log.txt", "a+") as mylogfile:
148 | mylogfile.write("TS = " + str(today) + '\n')
149 | mylogfile.write("EP = " + str(round(time.time(), 0)) + '\n')
150 | mylogfile.write("GPU temperature = " + get_gpu_tempfunc() + '\n')
151 | mylogfile.write("CPU temperature = " + get_cpu_tempfunc() + '\n')
152 | mylogfile.write("Cpu usage = " + get_cpu_use() + '\n')
153 | mylogfile.write("RAM usage = " + get_ram_info() + '\n')
154 | mylogfile.write("Swap usage = " + get_swap_info() + '\n')
155 | mylogfile.write("Raspberry pi temperature monitor: raspberrypi \n")
156 | # Add alarm message to log
157 | if (alarm_mode == "1") and get_cpu_tempfunc() > cpu_limit:
158 | mylogfile.write("Warning : cpu over the temperature limit: "
159 | + cpu_limit + '\n')
160 | # Send log by mail
161 | if (choice == "l") and (mail_alert == "1"):
162 | if (alarm_mode == "1") and get_cpu_tempfunc() > cpu_limit:
163 | mail_func(" Warning ", mailuser, destlog)
164 | except Exception as error:
165 | msg_func("red", "Problem with data logging convert function {}".format(error))
166 | msg_func("red", "Check that log file exists and is not corrupt ")
167 | else:
168 | msg_func("bold", "Logging function run completed")
169 |
170 |
171 | def mail_func(sub, mailu, destlog):
172 | """Function sends mail via ssmpt ,
173 | passed subject line and mail account name and logfile"""
174 | try:
175 | os.chdir(destlog)
176 | if os.path.exists("log.txt"):
177 | # Command to send mail from bash
178 | os.system('echo "Datafile log.txt attached" > body.txt')
179 | os.system('mpack -s "raspberry-PI-temperature {}" -d body.txt log.txt {}'.format(sub, mailu))
180 | else:
181 | msg_func("red", "Error: log file is not there {}".format(destlog))
182 | except Exception as error:
183 | msg_func("red", "Error with mail send function {}".format(error))
184 | msg_func("red", "Check that log file exists {}".format(destlog))
185 | msg_func("red", "Check that network is up")
186 | msg_func("red", "ssmtp and the user is configured properly {}".format(mailu))
187 | else:
188 | msg_func("bold", "Mail send function completed")
189 |
190 |
191 | def data_func(destlog, flag):
192 | """ Function to parse log file and produce a Data report
193 | also called from rpiTempmonGraph module when called
194 | from there exits before report build"""
195 | # parse logfile section
196 | # define lists to hold data from logfile
197 | timelist = []
198 | unixlist = []
199 | cpulist = []
200 | gpulist = []
201 | cpu_uselist = []
202 | ramlist = []
203 | swaplist = []
204 | my_warning_count = 0
205 |
206 | try:
207 | # get data from file and put into lists
208 | mypath = destlog + "/" + "log.txt"
209 | if os.path.isfile(mypath):
210 | with open(mypath, 'r') as myfile:
211 | for line in myfile:
212 | if "TS" in line:
213 | timelist.append(line[5:-1])
214 | if "EP" in line:
215 | unixlist.append(line[5:-1])
216 | if "CPU" in line:
217 | cpulist.append(line[18:20])
218 | if "GPU" in line:
219 | gpulist.append(line[18:20])
220 | if "Cpu" in line:
221 | cpu_uselist.append(line[12:16])
222 | if "RAM" in line:
223 | ramlist.append(line[12:16])
224 | if "Swap" in line:
225 | swaplist.append(line[13:17])
226 | if "Warning" in line:
227 | my_warning_count += 1
228 | else:
229 | msg_func("red", "Error: Log file not found at {}".format(mypath))
230 |
231 | if flag: # if called from graph module go back with data
232 | return timelist, unixlist, cpulist, gpulist, cpu_uselist, ramlist, swaplist
233 | # Report code section
234 | msg_func("bold", "\nRaspberry pi CPU GPU temperature monitor program")
235 | print("Data report on {} \n".format(mypath))
236 | print("Start temperature: {} Time: {}".format(cpulist[0], timelist[0]))
237 | length = len(cpulist)
238 | print("End temperature: {} Time: {}".format(cpulist[length-1], timelist[length-1]))
239 | print("Number of data points: {} \n".format(length))
240 | cpulist = list(map(int, cpulist))
241 | average_temp = sum(cpulist) / float(length)
242 | print("Average Temperature: {:.2f}".format(average_temp))
243 | print("Max Tempertaure: {}".format(max(cpulist)))
244 | print("Min Tempertaure: {}".format(min(cpulist)))
245 | print("Number of temperature warnings: {}\n".format(my_warning_count))
246 |
247 | cpu_uselist = list(map(float, cpu_uselist))
248 | average_use = sum(cpu_uselist) / float(length)
249 | print("Average Cpu usage: {:.2f}".format(average_use))
250 | print("Max Cpu usage: {}".format(max(cpu_uselist)))
251 | print("Min Cpu usage: {} \n".format(min(cpu_uselist)))
252 | ramlist = list(map(float, ramlist))
253 | average_use = sum(ramlist) / float(length)
254 | print("Average RAM usage: {:.2f}".format(average_use))
255 | print("Max RAM usage: {}".format(max(ramlist)))
256 | print("Min RAM usage: {}".format(min(ramlist)))
257 | except Exception as error:
258 | msg_func("red", error)
259 | msg_func("red", "Error in data_func function")
260 | return
261 |
262 |
263 | def stresstest(destlog, scancount):
264 | """ function to carry out stress test, creates a data csv file and
265 | optional graph"""
266 | # if not a positive int print message and ask for input again
267 | if int(scancount) < 2 or int(scancount) > 50:
268 | msg_func("red", "Error: input to -ST must be a positive integer 2-50")
269 | quit()
270 |
271 | try:
272 | yaxislist = []
273 | cpu_uselist = []
274 | cpulist = []
275 |
276 | # csv file
277 | os.chdir(destlog)
278 | mycsvpath = destlog + "/" + "stresslog.csv"
279 | output_file = open(mycsvpath, 'w', newline='')
280 | output_writer = csv.writer(output_file)
281 | print("RPi tempmon, CPU Stress Test:")
282 | # loop thru data for scancount carrying out cpu stress test
283 | if os.path.isfile(mycsvpath):
284 | for i in range(0, int(scancount)):
285 | yaxislist.append(i+1)
286 | cpu_temp = get_cpu_tempfunc()
287 | cpu_use = get_cpu_use()
288 | cpu_uselist.append(cpu_use)
289 | cpulist.append(cpu_temp)
290 | msg_func("bold", "Testrun: {}".format(i+1))
291 | # Sysbench stress test
292 | os.system('sysbench --test=cpu --cpu-max-prime=20000 --num-threads=4 run >/dev/null 2>&1')
293 | msg_func("bold", "CPU temp => {}'C".format(cpu_temp))
294 | msg_func("bold", "CPU usage => {}%".format(cpu_use))
295 | output_writer.writerow([i+1, cpu_temp, cpu_use])
296 | output_file.close()
297 | print("\ncsv file, stresslog.csv, created at {}".format(destlog))
298 | else:
299 | msg_func("red", "Error: Log file not found at {}".format(mycsvpath))
300 | except Exception as error:
301 | print("Problem with stress test function {}".format(error))
302 | quit()
303 | else:
304 | plotlabel1 = 'CPU temp'
305 | plotlabel2 = 'CPU usage'
306 | ylabel = 'CPU usage(%) / Temperature (degrees)'
307 | title = 'CPU temperature and usage , Stress Test results'
308 | return yaxislist, cpulist, cpu_uselist, plotlabel1, plotlabel2, ylabel, title
309 |
310 |
311 | def msg_func(myprocess, mytext):
312 | """NAME : msg_func
313 | DESCRIPTION :prints to screen
314 | prints line, text and anykey prompts, yesno prompt
315 | INPUTS : $1 process name $2 text input
316 | PROCESS :[1] print line [2] anykey prompt
317 | [3] print text "green , red ,blue , norm yellow and highlight"
318 | [4] yn prompt,
319 | OUTPUT yesno prompt return 1 or 0"""
320 |
321 | # colours for printf
322 | blue = '\033[96m'
323 | green = '\033[92m'
324 | yellow = '\033[93m'
325 | red = '\033[91m'
326 | bold = '\033[1m'
327 | end = '\033[0m'
328 | mychoice = ""
329 |
330 | if myprocess == "line": # print blue horizontal line of =
331 | print(blue + "="*80 + end)
332 |
333 | if myprocess == "anykey":
334 | input("Press to continue" + " " + mytext)
335 |
336 | if myprocess == "green": # print green text
337 | print(green + mytext + end)
338 |
339 | if myprocess == "red": # print red text
340 | print(red + mytext + end)
341 |
342 | if myprocess == "blue": # print blue text
343 | print(blue + mytext + end)
344 |
345 | if myprocess == "yellow": # print yellow text
346 | print(yellow + mytext + end)
347 |
348 | if myprocess == "bold": # print bold text
349 | print(bold + mytext + end)
350 |
351 | if myprocess == "yesno": # yes no prompt loop
352 | while True:
353 | mychoice = input("Repeat ? [y/n]")
354 | if mychoice == "y":
355 | choice = 1
356 | return choice
357 | elif mychoice == "n":
358 | choice = 0
359 | return choice
360 | else:
361 | print(yellow + "Please answer: y for yes OR n for no!" + end)
362 |
363 | return 2
364 |
365 |
366 | def importtest(text):
367 | """import print test statement"""
368 | # print(text)
369 | pass
370 | # ===================== MAIN ===============================
371 |
372 |
373 | if __name__ == '__main__':
374 | importtest("main")
375 | else:
376 | importtest("Imported {}".format(__name__))
377 |
378 | # ===================== END ===============================
379 |
--------------------------------------------------------------------------------
/rpiTempMod/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/rpiTempMod/__init__.py
--------------------------------------------------------------------------------
/rpiTempSrc/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gavinlyonsrepo/raspberrypi_tempmon/0aab47ac46a0640f5bd567998143da1b53cad90c/rpiTempSrc/__init__.py
--------------------------------------------------------------------------------
/rpiTempSrc/rpi_tempmon.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | """
3 | # ========================HEADER=======================================
4 | # title :rpi_tempmon.py
5 | # description :python script to display the CPU & GPU temp of RPi
6 | # author :Gavin Lyons
7 | # intial date :17/08/2017
8 | # web url :https://github.com/gavinlyonsrepo/raspberrypi_tempmon
9 | # mail :glyons66@hotmail.com
10 | # python version : 3.7.3
11 | # Functions:
12 | (1) exit_handler_func: exit handler function
13 | (2) stresstest_func: stresstest function
14 | (3) process_cmd_arguments: process command line arguments
15 | (4) print_title_func: prints title to console
16 | (5) read_configfile_func: read in config file
17 | (6) alarm_mode_check: checks for alarm condition
18 | (7) cont_mode_check: checks for continuous mode
19 | (8) main: main program loop
20 | (9) set_paths_func: set and check the paths for config & cache
21 | (10) led_toggle_func: function to toggle a GPIO
22 | """
23 | # =========================IMPORTS======================
24 | # Import the system modules needed to run rpi_tempmon.py.
25 | import sys
26 | import os
27 | import datetime
28 | import argparse
29 | import configparser
30 | import time
31 | import socket # For hostname
32 | from gpiozero import LED
33 |
34 | # my modules
35 | from rpiTempMod import RpiTempmonGraph
36 | from rpiTempMod import RpiTempmonWork as Work
37 | # Local testing, comment out next 3 lines for production
38 | #sys.path.insert(0, '/home/gavin/Documents/tech/mytemp/rpiTempMod')
39 | #import RpiTempmonWork as Work
40 | #import RpiTempmonGraph
41 |
42 | # =======================GLOBALS=========================
43 | # metadata
44 | __VERSION__ = "3.0"
45 | __URL__ = "https://github.com/gavinlyonsrepo/raspberrypi_tempmon"
46 | # set the path for logfile
47 | DESTLOG = os.environ['HOME'] + "/.cache/rpi_tempmon"
48 | # set the path for config file
49 | DESTCONFIG = os.environ['HOME'] + "/.config/rpi_tempmon"
50 | DESTCONFIGFILE = DESTCONFIG + "/" + "rpi_tempmon.cfg"
51 |
52 | # ===================FUNCTION SECTION===============================
53 |
54 |
55 | def exit_handler_func():
56 | """Handle the program exit"""
57 | # Print exit message
58 | print("\nGoodbye " + os.environ['USER'])
59 | Work.msg_func("bold", "Endex")
60 | quit()
61 |
62 |
63 | def stresstest_func(testrun_count):
64 | """Call stresstest function and display stress test graph choice"""
65 | print(DESTLOG)
66 | yaxislist, cpulist, cpu_uselist, plotlabel1, \
67 | plotlabel2, ylabel, title = Work.stresstest(DESTLOG, testrun_count)
68 | mychoice = input("\nDo you want to view graph of stress test? [y/N]")
69 | if (mychoice == "y") or (mychoice == "Y"):
70 | mygraph = RpiTempmonGraph.MatplotGraph("RPi Tempmon :")
71 | mygraph.draw_graph(yaxislist, cpulist, cpu_uselist,
72 | plotlabel1, plotlabel2, ylabel, title)
73 |
74 |
75 | def process_cmd_arguments():
76 | """Function for processing CL arguments
77 | return tuple of 4 values to main"""
78 | str_desc = "URL help at: {}".format(__URL__)
79 | parser = argparse.ArgumentParser(description=str_desc)
80 | parser.add_argument(
81 | '-v', help='Print rpi_tempmon version and quit',
82 | default=False, dest='version', action='store_true')
83 |
84 | parser.add_argument(
85 | '-l', help='Log file mode',
86 | default=False, dest='logfile', action='store_true')
87 |
88 | parser.add_argument(
89 | '-s', help=' convert Log file to csv',
90 | default=False, dest='csv_convert', action='store_true')
91 |
92 | parser.add_argument(
93 | '-L', help='Log folder mode',
94 | default=False, dest='logfolder', action='store_true')
95 |
96 | parser.add_argument(
97 | '-m', help='Send email mode with ssmtp',
98 | default=False, dest='mail', action='store_true')
99 |
100 | parser.add_argument(
101 | '-g', help='Draw a graph from log file data',
102 | default=False, dest='graphlog', action='store_true')
103 |
104 | parser.add_argument(
105 | '-c', help='Continuous mode, + integer arg in secs for delay time',
106 | type=int, dest='cont')
107 |
108 | parser.add_argument(
109 | '-a', help='Analysis the log file and report',
110 | default=False, dest='data', action='store_true')
111 |
112 | parser.add_argument('-n', help='notify mode +int arg, 2=notify always ,\
113 | 3=notify only on Cpu limit exceed', type=int, dest='notify')
114 |
115 | parser.add_argument(
116 | '-ST', help='Stress test mode, + integer arg for number of runs',
117 | type=int, dest='stresstest')
118 |
119 | args = parser.parse_args()
120 |
121 | # read config file
122 | config_file_data = read_configfile_func()
123 | # unpack tuple with values
124 | mailuser, mail_alert, alarm_mode, cpu_limit, led_mode, led_num = config_file_data
125 | # pack a new tuple for main
126 | config_file_data_main = config_file_data[2:6]
127 |
128 | # check the args
129 | if not len(sys.argv) > 1:
130 | # no argument > back to main
131 | return config_file_data_main
132 |
133 | if args.cont:
134 | # cont mode > back to main
135 | return config_file_data_main
136 |
137 | if args.stresstest:
138 | stresstest_func(sys.argv[2])
139 |
140 | if args.version:
141 | Work.msg_func("bold", "\nVersion : rpi_tempmon " + __VERSION__)
142 |
143 | if args.csv_convert:
144 | Work.csv_convert(DESTLOG)
145 |
146 | if args.logfolder:
147 | # call log function pass L for folder
148 | Work.logging_func("L", "0", "100", "X", DESTLOG, False)
149 |
150 | if args.data:
151 | # call the data analysis function
152 | Work.data_func(DESTLOG, False)
153 |
154 | if args.logfile:
155 | # call log function pass l for file
156 | Work.logging_func("l", mail_alert, cpu_limit, mailuser, DESTLOG, alarm_mode)
157 |
158 | if args.notify:
159 | Work.notify_func(sys.argv[2], cpu_limit)
160 |
161 | if args.mail:
162 | # mail mode
163 | if mail_alert == "1":
164 | Work.mail_func(" Mail mode ", mailuser, DESTLOG)
165 | else:
166 | Work.msg_func("yellow", "Mail Alert disabled in config file rpitempmon.cfg ::" + mail_alert)
167 |
168 | if args.graphlog:
169 | mygraph = RpiTempmonGraph.MatplotGraph("RPi Tempmon :")
170 | mygraph.graph_log_data(DESTLOG)
171 |
172 | exit_handler_func()
173 |
174 |
175 | def print_title_func():
176 | """Function to print title in normal and cont mode"""
177 | os.system('clear')
178 | print()
179 | Work.msg_func("line", "")
180 | today = datetime.datetime.now().strftime("%y-%m-%d %H:%M:%S")
181 | Work.msg_func("bold", "Raspberry pi CPU GPU temperature monitor program")
182 | print(today)
183 | print(socket.gethostname())
184 | Work.msg_func("line", "")
185 | Work.msg_func("green", "CPU temp => {}'C".format(Work.get_cpu_tempfunc()))
186 | Work.msg_func("green", "GPU temp => {}".format(Work.get_gpu_tempfunc()))
187 | Work.msg_func("green", "CPU usage => {}%".format(Work.get_cpu_use()))
188 | Work.msg_func("green", "RAM usage => {}%".format(Work.get_ram_info()))
189 | Work.msg_func("green", "Swap usage => {}% \n".format(Work.get_swap_info()))
190 |
191 |
192 | def read_configfile_func():
193 | """Read in configfile, return 6 values in a tuple"""
194 | try:
195 | myconfigfile = configparser.ConfigParser()
196 | myconfigfile.read(DESTCONFIGFILE)
197 | mailuser = myconfigfile.get("MAIN", "RPI_AuthUser")
198 | mail_alert = myconfigfile.get("MAIN", "MAIL_ALERT")
199 | alarm_mode = myconfigfile.get("MAIN", "ALARM_MODE")
200 | cpu_limit = myconfigfile.get("MAIN", "CPU_UPPERLIMIT")
201 | led_mode = myconfigfile.get("MAIN", "LED_MODE")
202 | led_num = myconfigfile.get("MAIN", "GPIO_LED")
203 | if (int(cpu_limit) < 1) or (int(cpu_limit) > 99):
204 | Work.msg_func("red", " ERROR : ")
205 | print("CPU_UPPERLIMIT must be between 1 and 99")
206 | quit()
207 | config_file_data = (mailuser, mail_alert, alarm_mode, cpu_limit, led_mode, led_num)
208 | except Exception as error:
209 | Work.msg_func("red", " ERROR : ")
210 | print("Problem with config file: {}".format(error))
211 | print("Must have section header of [MAIN] and six parameters")
212 | print("Help and example file at url")
213 | print(__URL__)
214 | exit_handler_func()
215 |
216 | return config_file_data
217 |
218 |
219 | def alarm_mode_check(config_file_data_main, my_led):
220 | """function to check alarm mode """
221 | alarm_mode, cpu_limit, led_mode, led_num = config_file_data_main
222 | if alarm_mode == "1":
223 | Work.msg_func("bold", "Alarm mode is on: " + cpu_limit + "'C")
224 | if Work.get_cpu_tempfunc() > cpu_limit:
225 | # display led mode
226 | Work.msg_func("red", "Warning : CPU over the temperature limit: " + cpu_limit + "'C")
227 | if led_mode == "1":
228 | Work.msg_func("bold", "LED mode is on, GPIO pin selected: " + led_num)
229 | led_toggle_func("on", my_led)
230 |
231 |
232 | def cont_mode_check(my_led):
233 | """check for continuous mode """
234 | delay = 5
235 | # is their an argument?
236 | if len(sys.argv) > 1:
237 | # Was it -c?
238 | if sys.argv[1] == "-c":
239 | val = int(sys.argv[2])
240 | # if not a positive int print message and ask for input again
241 | if val < 0:
242 | print("Sorry, input to -c must be a positive integer.")
243 | quit()
244 | delay = sys.argv[2]
245 | Work.msg_func("bold", "Continuous mode: On")
246 | Work.msg_func("bold", "Sleep delay set to: " + str(delay) + " seconds.")
247 | print("\nPress CTRL+c to quit.")
248 | time.sleep(float(delay))
249 | os.system('clear')
250 | else: # normal mode prompt
251 | if Work.msg_func("yesno", ""):
252 | pass
253 | else:
254 | led_toggle_func("off", my_led)
255 | exit_handler_func()
256 |
257 |
258 | def set_paths_func():
259 | """set and check the paths for config file and cache/logging"""
260 | if not os.path.exists(DESTLOG): # Check cache path is there
261 | os.makedirs(DESTLOG)
262 | if not os.path.exists(DESTCONFIG): # Check config path is there
263 | os.makedirs(DESTCONFIG)
264 |
265 | if os.path.isfile(DESTCONFIGFILE): # Check config file is there
266 | pass
267 | else:
268 | try:
269 | config = configparser.ConfigParser()
270 | config.optionxform = lambda option: option # case sensitive option
271 | config['MAIN'] = {'RPI_AuthUser': 'example@gmail.com',
272 | 'MAIL_ALERT': '1',
273 | 'ALARM_MODE': '1',
274 | 'CPU_UPPERLIMIT': '70',
275 | 'LED_MODE': '1',
276 | 'GPIO_LED': '26'}
277 | with open(DESTCONFIGFILE, 'w') as configfile:
278 | config.write(configfile)
279 | except Exception as error:
280 | Work.msg_func("red", " ERROR : ")
281 | print("Config file is missing at {}".format(DESTCONFIGFILE))
282 | print("Problem Creating default config file: {}".format(error))
283 | print("Example config file and info can be found at:")
284 | print(__URL__)
285 | exit_handler_func()
286 | else:
287 | Work.msg_func("yellow", " INFO : ")
288 | print("Config file was missing at {}".format(DESTCONFIGFILE))
289 | print("Config file created with default values.")
290 | input("Press to continue")
291 |
292 |
293 | def led_toggle_func(mode, my_led):
294 | """ led_toggle_func , function
295 | to toggle a GPIO LED, passed mode on/off
296 | and GPIO pin of LED"""
297 | time.sleep(.05) # gpio init
298 | if mode == "off":
299 | my_led.off()
300 | else:
301 | my_led.on()
302 |
303 |
304 | def main(config_file_data_main):
305 | """Function to hold main program loop, param1 : config file data tuple"""
306 |
307 | # unpack config file data main tuple
308 | alarm_mode, cpu_limit, led_mode, led_num = config_file_data_main
309 | my_led = LED(int(led_num))
310 | scan_count = 0
311 |
312 | # main loop
313 | try:
314 | while True:
315 | print_title_func()
316 | scan_count += 1
317 | Work.msg_func("bold", "Number of scans: " + str(scan_count))
318 | alarm_mode_check(config_file_data_main, my_led)
319 | cont_mode_check(my_led)
320 | led_toggle_func("off", my_led)
321 | except KeyboardInterrupt:
322 | led_toggle_func("off", my_led)
323 | exit_handler_func()
324 |
325 |
326 | # =====================MAIN===============================
327 | if __name__ == "__main__":
328 | try:
329 | print("\nRPi temperature monitor:")
330 | set_paths_func()
331 | exit(main(process_cmd_arguments()))
332 | except (KeyboardInterrupt, SystemExit):
333 | pass
334 | # ====================END===============================
335 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | setup(
4 | name="rpi_tempmon.py",
5 | version="3.0",
6 | author="gavin lyons",
7 | author_email="glyons66@hotmail.com",
8 | description="Monitor RAM,CPU and GPU data of Raspberry Pi",
9 | license=" GPL",
10 | keywords="PI Raspberry CPU ARM GPU temperature temp rpi monitor display gavin lyons",
11 | url="https://github.com/gavinlyonsrepo/raspberrypi_tempmon",
12 | download_url='https://github.com/gavinlyonsrepo/raspberrypi_tempmon/archive/3.0.tar.gz',
13 | packages=['rpiTempSrc','rpiTempMod',],
14 | install_requires= ['matplotlib','pip','psutil'],
15 | setup_requires = ['pip'],
16 | scripts=['rpiTempSrc/rpi_tempmon.py'],
17 | classifiers=[
18 | "Topic :: Utilities",
19 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
20 | ],
21 | )
22 |
--------------------------------------------------------------------------------