LanmiLab
Published

Smart Lighting Using PiJuice Zero and Raspberry Pi Zero

Make smart dimmable uninterruptible lighting system controlled by Raspberry Pi Zero W using high brightness LED powered by PiJuice Zero UPS.

IntermediateFull instructions provided3 hours2,290
Smart Lighting Using PiJuice Zero and Raspberry Pi Zero

Things used in this project

Hardware components

Raspberry Pi Zero W
Raspberry Pi needs to have per-soldered 40-pin GPIO header.
×1
PiJuice Zero
PiJuice Zero
×1
PiJuice 5000mAh Battery
PiJuice 5000mAh Battery
×1
Power Supply 2.5A
×1
MOSFET N-Channel IRLB8748
×1
Resistor 0.47 ohm
×1
Jumper wire
×1
LED Lamp
×1
Spacer M2.5 4mm
×4
Bolt M2.5 12mm
×8
Washer M2.5
×4
Hex nut M2.5
×4
microSDHC 8GB Class 10
×1

Story

Read more

Schematics

Schematic

PiJuice Light schematic depicting connections between system parts.

Code

pjlight.py

Python
Light ontrol Server Python Script
#!/usr/bin/env python3

# pjlight.py - Lighting server script based on Tornado websockets
# runs on Raspberry Pi computer as part of blog demo project "Smart Lighting using PiJuice Zero and Raspberry Pi Zero"
# author: LanmiLab, twitter.com/lanmiLab
from pijuice import PiJuiceInterface,PiJuice
import time, threading
import os.path
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web

pijuice = PiJuice(1,0x14)

#Web application directory path (Index.html)
root = '/boot' #os.path.dirname(__file__)

#Tonado server port
PORT = 80

CURRENT_LIMITER_RESISTANCE = 0.47 # Resistance of current limiting resistor used
LED_VOLTAGE_AT_1A = 2.95 # LED forvard voltage at current of 1A through LED, from LED datasheet
LED_VOLTAGE_AT_2A = 3.15 # LED forvard voltage at current of 2A through LED, from LED datasheet
MAX_LED_CURRENT = 1.6 # desired maximum current through LED, current will be dimmable in range 0 - MAX_LED_CURRENT, maximum possible value is 2.1A, depends on current limiting resistor and LED forward voltage
MAX_LED_CURRENT_FROM_BAT = 1.5 # desired maximum current through LED when system powered only from battery, settings value depends on battery capacity, current limiting resistor and LED forward voltage
LED_IV_COEFF_AT_1A = (LED_VOLTAGE_AT_2A-LED_VOLTAGE_AT_1A)/1

state = 'OFF'
level = 0.0
sourceStatus = 'NOT_PRESENT'

def SetLedLevel():
	global level
	ret = pijuice.status.GetBatteryVoltage()
	if ret['error']=='NO_ERROR':
		bat_voltage = ret['data']/1000
	else:
		bat_voltage = 3.8
		
	ret = pijuice.status.GetStatus()
	if ret['error']=='NO_ERROR' and (ret['data']['powerInput'] == 'PRESENT' or ret['data']['powerInput5vIo'] == 'PRESENT') and bat_voltage > 3.8:
		maxLedCurrent = MAX_LED_CURRENT
		sourceStatus = 'PRESENT'
	else:
		maxLedCurrent = MAX_LED_CURRENT_FROM_BAT
		sourceStatus = 'NOT_PRESENT'

	current_req = level*maxLedCurrent
	duty_cycle = 100*current_req*(CURRENT_LIMITER_RESISTANCE+0.07)/(bat_voltage-(LED_VOLTAGE_AT_1A+LED_IV_COEFF_AT_1A*(current_req-1.0)))
	duty_cycle = 100 if duty_cycle > 100 else duty_cycle
	duty_cycle = 0 if duty_cycle < 0 else duty_cycle

	pijuice.status.SetIoPWM(2, duty_cycle)
	print('LEVEL SET:', level)

def ControlLoop():
	global sourceStatus
	ret = pijuice.status.GetStatus()
	if ret['error']=='NO_ERROR' and (ret['data']['powerInput'] == 'PRESENT' or ret['data']['powerInput5vIo'] == 'PRESENT'):
		if sourceStatus != 'PRESENT':
			SetLedLevel() # update level if input source becomes present
			sourceStatus = 'PRESENT'
	else:
		if sourceStatus != 'NOT_PRESENT':
			SetLedLevel() # update level if input source becomes not present
			sourceStatus = 'NOT_PRESENT'
			
	print(ret['data']['battery'])
	threading.Timer(2.0, ControlLoop).start()

class MainHandler(tornado.web.RequestHandler):
	def get(self):
		print("[HTTP](MainHandler) New Connection.")
		self.render("index.html")

class WSHandler(tornado.websocket.WebSocketHandler):
	def open(self):
		print('[WS] Connection opened.')

	def on_message(self, message):
		global state
		global level
		print('[WS] Incoming message:', message)
		if 'TOGGLE' in message:
			if state=='OFF':
				pijuice.power.SetSystemPowerSwitch(2100)
				state='ON'
			else:
				pijuice.power.SetSystemPowerSwitch(0)
				state='OFF'
		if 'LEVEL' in message:
			level = int(message.split(':')[1])/255
			SetLedLevel()
			print('LEVEL COMMAND', level)

	def on_close(self):
		print('[WS] Connection closed.')

		
application = tornado.web.Application([
	(r'/', MainHandler),
	(r'/pjlight', WSHandler),
	], {"path": root})

	
if __name__ == "__main__":
	try:
		http_server = tornado.httpserver.HTTPServer(application)
		http_server.listen(PORT)
		
		ret = pijuice.config.SetIoConfiguration(2, {'mode': 'PWM_OUT_PUSHPULL', 'pull': 'NOPULL', 'period': 5000, 'duty_cycle': 0.1})
		swstate = pijuice.power.GetSystemPowerSwitch()
		if swstate['error'] == 'NO_ERROR':
			state = 'ON' if swstate['data'] != 0 else 'OFF'
			
		ControlLoop()
		
		print("PiJuice Light Server started, state: ", state)
		
		main_loop = tornado.ioloop.IOLoop.instance()
		main_loop.start()
	except:
		print("Exception triggered - PiJuice Light Server stopped.")

index.html

HTML
Light control Web Application
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>PiJuice Light</title>

</head>
<body>
  <div id='page-wrapper' style='width:100%; font-size: 1.8vw; background: linear-gradient(black, gray); font-family:Arial; color:#999999'>
	<a href="https://twitter.com/lanmiLab" style='text-decoration: none;color:#999999'>twitter.com/lanmiLab</a>
	<div style='overflow:hidden;width:100%;float:left;border-style:solid;border-width:1px;border-radius:5px; box-sizing: border-box;'>
		<div style='float:left;height:100%;margin-left:0.5%;margin-top:1%;'><h1>PiJuice Light</h1></div>
		<span id='temperature' style='float:left;height:100%;margin-left:3%;margin-top:4%;'></span>
		<span id='humidity' style='float:left;height:100%;margin-left:3%;margin-top:4%;'></span>
		<span id='status'style='float:right;height:100%;margin-right:2%;margin-top:4%;'>Connecting...</span>
	</div>
	<div id='container' style='width:100%;margin:auto;'>
		<div style='overflow:hidden; width100%; position:relative; margin:auto; padding:0px; border-style: solid;border-width: 1px;border-radius:5px;margin:5px 0px; background-color: #222123;'>
			<div style='overflow:hidden; width:100%' >
				<div style='width:15%; margin:0.5%; float:left; font-weight: bold;' class='appTitle'></div>
				<div style='width:15%; margin:0.5%; float:left; font-weight: bold;' class='deviceAddr'></div>
			</div>
			<div class='toggleCtrlContainer' style=' width:16.67%; float:left;'>
				<div style='width:100%;margin:0; padding:0px; position: relative; border-style: solid;border-width: 1px; box-sizing: border-box;'>
					<div style='display:block; padding-top:100%;'></div>
					<button type='button' class='toggleButton' onclick='SendToggleCommand()' style='position:absolute; width:90%; height:90%; top:5%; left:5%;  font-weight: bold; font-size:1.5em; color: #cccccc;'>ON/OFF</button>
				</div>
			</div>
			<div style=' width:83.33%; float:left;'>
				<div style='width:100%;margin:0; padding:0px; position: relative; font-weight: bold; color: #cccccc; border-style: solid;border-width: 1px;'>
					<div style='display:block; padding-top:20%;'></div>
					<span style='position: absolute; top:5%; left:2%;' >Level</span>
					<input id='levelCtrlElem' style='position: absolute; top:25%; left:2%; width:95%; height:60%; padding:0;' type='range' min='1' max='255' ></button>
				</div>
			</div>
		</div>
	</div>
  </div>
</body>
    <script type="text/javascript">  
			
	var socket;
	var light_state = 'pjlight_off'
	var light_level = 128
	
	window.onload = function() {

		// Create a new WebSocket.
		if(window.location.protocol == "http:"){
			socket = new WebSocket("ws://" + window.location.host + "/pjlight");
		}
		else if(window.location.protocol == "https:"){
			socket = new WebSocket("wss://" + window.location.host + "/pjlight");
		}
		socket.binaryType = "arraybuffer";
		
		var rangeCtrl = document.getElementById("levelCtrlElem");
		rangeCtrl.onchange = function() {
			SendLevelControlCommand(rangeCtrl.value);
		};

		// Show a connected message when the WebSocket is opened.
		socket.onopen = function(event) {

			var socketStatus = document.getElementById('status');
			socketStatus.innerHTML = 'Connected to: ' + event.currentTarget.url;
			socketStatus.className = 'open';
			
			// Handler for messages sent by the server.
			socket.onmessage = WsReceive;

		};
	};
	
	function WsReceive(event)
	{
		var msg = new Uint8Array(event.data);

		switch (msg[0]) // look at response message id
		{
		case 0: // mt command response
			break;
			
		case 1: // sensor data message
			break;
		}
	};

	function SendToggleCommand() {
		msg = "TOGGLE;LEVEL:"+light_level
		socket.send(msg);
		console.log("Command sent ", msg);
	}
	
	function SendLevelControlCommand(level) {
		socket.send("LEVEL:"+level);
		light_level=level
		console.log(level)
	}

  </script>
</html>

pjlight.service

BatchFile
System Service that automatically runs Lighting server on boot.
[Unit]
Description=PiJuice status service
Wants=network-online.target
After=network.target network-online.target

[Service]
Type=idle
WorkingDirectory=/boot/
ExecStart=/usr/bin/python3 /boot/pjlight.py
ExecStopPost=/usr/bin/python3 /boot/pjlight.py stop
Restart=always

[Install]
WantedBy=multi-user.target

Credits

LanmiLab
5 projects • 5 followers
Engineer, Researcher, Maker, Embedded Systems Consultant. If you require any help with your project or idea, feel free to contact me.
Contact

Comments

Please log in or sign up to comment.