-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpowermonitor.py
More file actions
executable file
·387 lines (293 loc) · 9.85 KB
/
powermonitor.py
File metadata and controls
executable file
·387 lines (293 loc) · 9.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
#!/usr/bin/python3
# coding=utf-8
# -*- coding: utf-8 -*-
"""
This code reads the RVR battery voltage and shows the values on an OLED
via SSD1306 with a nice graphical battery level symbol. If
the battery level is lower than 50%, ... [tbd].
It also shows the hostname, the IP and the time and CPU temoerature.
It also checks a pushbotton state, connected to BCM 17 on Raspberry Pi 3
via 10k pull-down resistor. If pushed, it calls the "shutdown now" command.
"""
# for battery empty alarm
batteryEmptyLevel = 50 # below 50% means it is empty
batteryIsEmpty = False
# wait time in seconds between different display information
waitTime = 2
# wait time for a piezo beep
waitTimePiezo = 0.2
# for time and sleep
import time
# for OLED
import board
import digitalio
from PIL import Image, ImageDraw, ImageFont
import adafruit_ssd1306
# for joystick is connected check (path/file exists)
import os.path
# Define the Reset Pin
oled_reset = digitalio.DigitalInOut(board.D4)
# OLED resolution
WIDTH = 128
HEIGHT = 32 # Change to 64 if needed
BORDER = 1
# Use for I2C.
i2c = board.I2C()
oled = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c, addr=0x3C, reset=oled_reset)
# --------------------
# for signal handling
# --------------------
import signal
import sys
# my signal handler
def sig_handler(_signo, _stack_frame):
# LED OFF (low active!)
GPIO.output(ledPin, GPIO.HIGH)
## GPIO cleanup
GPIO.remove_event_detect(switchPin)
GPIO.cleanup()
# clear display
oled.fill(0)
oled.show()
print("powermonitor terminated clean.")
sys.exit(0)
# signals to be handled
signal.signal(signal.SIGINT, sig_handler)
signal.signal(signal.SIGHUP, sig_handler)
signal.signal(signal.SIGTERM, sig_handler)
# ----------------------
# GPIO/pushbotton stuff
# ----------------------
import RPi.GPIO as GPIO
# for poweroff
from subprocess import call
# init
GPIO.setmode(GPIO.BCM) # use the GPIO names, _not_ the pin numbers on the board
# Raspberry Pi pin configuration:
# pins BCM BOARD
switchPin = 17 # pin 11 (pushbutton)
ledPin = 27 # pin 13
piezoPin = 13 # pin 33
# setup
print('setup...')
GPIO.setup(switchPin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # waits for LOW
GPIO.setup(ledPin, GPIO.OUT)
GPIO.setup(piezoPin, GPIO.OUT)
# LED OFF (low active!)
GPIO.output(ledPin, GPIO.HIGH)
# checker
buttonPressed = False
# piezo beep function
def beep(numberBeeps):
for x in range(0, numberBeeps):
# Piezo OFF
GPIO.output(piezoPin, GPIO.HIGH)
# wait
time.sleep(waitTimePiezo)
# Piezo ON (low active!)
GPIO.output(piezoPin, GPIO.LOW)
# "wait" (generate a square wave for the piezo)
time.sleep(waitTimePiezo)
# switch detection by interrupt, falling edge, with debouncing
def my_callback(answer):
# LED ON (low active!)
GPIO.output(ledPin, GPIO.LOW)
print("Shutdown button on GPIO " + str(answer) + " pushed.")
# clear display
oled.fill(0)
oled.show()
# show some shutdown text on OLED
# send message to all users
call('wall +++ Shutting down Pi in 5 seconds +++', shell=True)
# delay
time.sleep(5)
# power off
call('sudo shutdown --poweroff "now"', shell=True)
# add button pressed event detector
print('registering event handler...')
GPIO.add_event_detect(switchPin, GPIO.FALLING, callback=my_callback, bouncetime=200)
# ----------------------
# OLED stuff
# ----------------------
# Clear display.
oled.fill(0)
oled.show()
# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
image = Image.new("1", (oled.width, oled.height))
# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)
# Draw a white background
draw.rectangle((0, 0, oled.width, oled.height), outline=255, fill=255)
# The fonts and sizes
size = 15
symbolWidth = 28
# text
fontText = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', size)
# https://fontawesome.io
# install via sudo 'sudo apt install fonts-font-awesome'
fontSymbol = ImageFont.truetype('/usr/share/fonts/truetype/font-awesome/fontawesome-webfont.ttf', size)
# ----------------------
# Voltage stuff
# ----------------------
# the battery symbols
batteryEmpty = chr(0xf244) # <25% = minVoltage
# battery level (white rectangle in empty battery symbol
maxRectLength = 16
# min and max voltages
measuredVoltage = 0.0
minVoltage = 3*3.3 # 3S LiPo-Battery with 3 x 3.3Volt = 9.9 Volt (empty battery)
maxVoltage = 3*4.2 # 3S LiPo-Battery with 3 x 4.2Volt = 12.6 Volt (full battery)
# the AD converter object
#adc = Adafruit_ADS1x15.ADS1015()
# Gain 1 means, max a value of +4.096 Volt (+4,096 Volt in Europe) on the ADC channel, resulting in a 'value' of +2047.
#GAIN = 1
# ----------------------
# network stuff
# ----------------------
# for getting the hostname and IP of the underlying system
import socket
# the network symbol
networkSymbol = chr(0xf1eb) # fa-wifi
# -------------------------------
# CPU temperature and time stuff
# -------------------------------
import os
# the time symbol
timeSymbol = chr(0xf017) # fa-clock-o
# the temperature symbol
tempSymbol = chr(0xf21e) # fa-heartbeat 0xf2db
# the joystick symbol
joySymbol = chr(0xf11b) # fa-gamepad
def getCpuTemperature():
tempFile = open("/sys/class/thermal/thermal_zone0/temp")
cpu_temp = tempFile.read()
tempFile.close()
return float(cpu_temp)/1000
# let's go
print('ready.')
# --------------
# the main lopp
# --------------
while (1):
# ------------------
# Network data
# ------------------
# clear OLED
# Draw a black filled box to clear the image.
draw.rectangle((0, 0, oled.width, oled.height), outline=0, fill=0)
# get hostname
hostname = socket.gethostname()
# get IP
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(('10.255.255.255', 1))
ip = s.getsockname()[0]
except:
ip = '127.0.0.1'
finally:
s.close()
# Write lines of text to display
# line 1, network symbol
draw.text((0, 0), networkSymbol, font=fontSymbol, fill=255)
# line 1, hostname, after symbol
draw.text((symbolWidth, 0), hostname, font=fontText, fill=255)
# line 2, IP
draw.text((0, size), ip, font=fontText, fill=255)
# Display image.
oled.image(image)
oled.show()
# wait some seconds and/or beep
if batteryIsEmpty is True:
# print('BATTERY is EMPTY.')
# beep n times
beep(5)
else:
time.sleep(waitTime)
# --------------------------
# Battery display
# -------------------------
# clear OLED
# Draw a black filled box to clear the image.
draw.rectangle((0, 0, oled.width, oled.height), outline=0, fill=0)
# read AD converter (battery voltage)
# use channel 0 on IC
# value = adc.read_adc(0, gain=GAIN)
value = 175
# 13.44 Volt battery voltage resulted in 2,94 Volt on the ADC channel with my circuit (voltage divider w/ two resistors (39k + 11k)).
# This resulted in a ADC 'value' of 1465.
# The conversion factor for the battery voltage is then: 1465 / 13.44 = 109.00297619047619
#
measuredVoltage = (value / 109.00297619047619)
if measuredVoltage < 0:
measuredVoltage = 0
# print("Value: %d" % value)
# print("Battery: %.1f Volt" % voltage)
# percent calculation
convertedVoltage = measuredVoltage - minVoltage
percent = convertedVoltage / (maxVoltage-minVoltage) * 100
if percent < 0:
percent = 0
# --------------------------------------
# this is for the battery alarn / piezo
# --------------------------------------
if percent < batteryEmptyLevel:
batteryIsEmpty = True
else:
batteryIsEmpty = False
# rectangle in battery symbol
rectLength = round(percent * maxRectLength / 100, 0)
# Write lines of text to display
# line 1, empty battery symbol
draw.text((0, 0), batteryEmpty, font=fontSymbol, fill=255)
# add filling level as filled rectangle
# empty: draw.rectangle((1, 3, 1, 11), outline=255, fill=255)
# full: draw.rectangle((1, 3, 16, 11), outline=255, fill=255)
draw.rectangle((1, 3, rectLength, 11), outline=255, fill=255)
# line 1, text after symbol
string = ("%.0f %%" % round(percent, 2))
draw.text((symbolWidth, 0), string, font=fontText, fill=255)
# line 2
draw.text((0, size), str("%.2f Volt" % measuredVoltage), font=fontText, fill=255)
# Display image.
oled.image(image)
oled.show()
# wait some seconds and/or beep
if batteryIsEmpty is True:
# print('BATTERY is EMPTY.')
# beep n times
beep(5)
else:
time.sleep(waitTime)
# ---------------------------------------
# Time and CPU temp and joystick display
# ---------------------------------------
# clear OLED
# Draw a black filled box to clear the image.
draw.rectangle((0, 0, oled.width, oled.height), outline=0, fill=0)
# get time
timeString = time.strftime("%H:%M", time.localtime(time.time()) )
# line 1, joystick symbol if connected or clock symbol
if os.path.exists("/dev/input/js0"):
draw.text((0, 0), joySymbol, font=fontSymbol, fill=255)
else:
draw.text((0, 0), timeSymbol, font=fontSymbol, fill=255)
# line 1, text after symbol
draw.text((symbolWidth, 0), timeString, font=fontText, fill=255)
# line 2, temp symbol
draw.text((0, size), tempSymbol, font=fontSymbol, fill=255)
# line 2, text after symbol
draw.text((symbolWidth, size), str(round(getCpuTemperature(), 1)) + " " + u'\N{DEGREE SIGN}' + "C", font=fontText, fill=255)
# Display image.
oled.image(image)
oled.show()
# wait some seconds and/or beep
if batteryIsEmpty is True:
# print('BATTERY is EMPTY.')
# beep n times
beep(5)
else:
time.sleep(waitTime)
# wtf?
print('This line should never be reached')