12 Commits

Author SHA1 Message Date
12ed601446 added notice 2017-12-13 16:32:23 +01:00
Giovanni Harting
bf4dcfaaf8 Update README.md 2017-01-31 07:43:42 +01:00
Giovanni Harting
7d6d289178 fixed wrong calculation of read pwm frequency
some more error handling
2015-11-14 14:35:42 +01:00
Giovanni Harting
db379ccdf0 reverted some derps 2015-11-13 23:58:41 +01:00
Giovanni Harting
99a7252194 added default val for pwm_freq 2/2 2015-11-13 23:56:09 +01:00
Giovanni Harting
a99d879298 added default val for pwm_freq 2015-11-13 23:51:53 +01:00
Giovanni Harting
6ae2cc4fb8 added some more error handling
fixed pwm_freq not getting set on init
2015-11-13 23:41:38 +01:00
Giovanni Harting
a65bb770fd added error handling when color can't get read 2015-11-13 23:17:49 +01:00
Giovanni Harting
02f6837d27 removed more unnecessary debug output 2015-11-13 23:13:17 +01:00
Giovanni Harting
df90bc8a68 fixed some bugs in the installation description 2015-11-13 22:25:45 +01:00
Giovanni Harting
f9d696b1b3 removed more unnecessary debug output 2015-10-31 22:47:09 +01:00
Giovanni Harting
aadcfafd00 added error handling of oserror 70 mostly caused by a busy i2c line
need to add a proper rate limiting and requeue of requests for a future release
2015-10-31 22:41:07 +01:00
4 changed files with 78 additions and 27 deletions

View File

@@ -2,13 +2,15 @@
[![][cq img]][cq] [![][license img]][license]
LedD is a daemon for interfacing LED stripes written in python3. It provides an abstract interface for effects to control any kind of LED stripe through an controller, although it is intented to be used with the PCA9685 chip. An Android application can connect and natively change any settings for the effects and stripes.
# DEPRECATED
## Goals
This project is no longer maintained and has been superseded by LedD.Go.
- manage multiple stripes and controllers at the same time
- an open effects github repository with simple download-and-run system
- automatic enumeration of the connected controller devices, restart/reset and status querys
## General
LedD is a multipurpose daemon for interfacing LED(s) written in python. It provides an abstract interface for effects to control any kind of LED through an backend, although its original purpose was interfacing a PCA9685 chip via i2c. Since there are multiple ways to control leds we made the decision to write LedD as open as possible to other ways of controlling leds. As a result of this decision we split LedD in a server part (this repository) and multiple clients, which can be written in any kind of language and can use a custom way to control its LEDs, as long as they implement LedD's protobuf protocol they can be controled with LedD.
As for frontends there is only an Android app available at this time, there will be more some time in the future. (You are encouraged to write your own!)
## Requirements
@@ -22,9 +24,10 @@ LedD is a daemon for interfacing LED stripes written in python3. It provides an
Make sure your i2c devices are available (modprobe i2c-dev) before you follow these steps.
1. `apt-get install python3-pip python3-cffi python3-docopt python3-nose python3-sqlalchemy python-smbus`
2. `pip3 install coloredlogs spectra json-rpc cffi smbus-cffi`
3. `adduser $USER i2c`
1. `apt-get install python3-dev python3-pip python3-cffi python3-docopt python3-nose python3-sqlalchemy python-smbus libffi-dev`
2. `pip3 install -U cffi` (fixes a bug where smbus-cffi can't install)
3. `pip3 install coloredlogs spectra json-rpc smbus-cffi`
4. `adduser $USER i2c`
### Plugins & Effects

View File

@@ -14,12 +14,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import errno
import logging
import time
import smbus
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import relationship, reconstructor
import smbus
from . import Base
@@ -50,7 +51,7 @@ class Controller(Base):
i2c_device = Column(Integer)
address = Column(String)
stripes = relationship("Stripe", backref="controller")
_pwm_freq = Column("pwm_freq", Integer)
_pwm_freq = Column("pwm_freq", Integer, default=1526)
"""
A controller controls a number of stripes.
@@ -61,12 +62,14 @@ class Controller(Base):
self._mode = None
self.bus = smbus.SMBus(self.i2c_device)
self._address = int(self.address, 16)
self.pwm_freq = self._pwm_freq
@reconstructor
def init_on_load(self):
self._mode = None
self.bus = smbus.SMBus(self.i2c_device)
self._address = int(self.address, 16)
self.pwm_freq = self._pwm_freq
def __repr__(self):
return "<Controller stripes={} cid={}>".format(len(self.stripes), self.id)
@@ -87,7 +90,13 @@ class Controller(Base):
return corrected
def get_channel(self, channel):
return self.bus.read_word_data(self._address, LED0_OFF_L + 4 * channel) / 4095
try:
return self.bus.read_word_data(self._address, LED0_OFF_L + 4 * channel) / 4095
except OSError as e:
if int(e) == errno.ECOMM:
return 0
else:
raise
def reset(self):
self.mode = int("0b00100001", 2) # MODE1 -> 0b00000001
@@ -108,7 +117,7 @@ class Controller(Base):
@property
def pwm_freq(self):
self._pwm_freq = (self.bus.read_byte_data(self._address, PCA9685_PRESCALE) + 1) / 4096 * 25000000
self._pwm_freq = round(390625 / ((self.bus.read_byte_data(self._address, PCA9685_PRESCALE) + 1) * 64))
return self._pwm_freq
@pwm_freq.setter
@@ -134,3 +143,6 @@ class Controller(Base):
'i2c_device': self.i2c_device,
'mode': self.mode
}
def close(self):
self.bus.close()

View File

@@ -14,25 +14,26 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
import configparser
import os
import sys
import asyncio
import configparser
import errno
import logging
import os
import signal
import sys
from sqlalchemy import create_engine
import spectra
from jsonrpc import JSONRPCResponseManager, dispatcher
from jsonrpc.exceptions import JSONRPCError, JSONRPCInvalidParams
import spectra
from sqlalchemy import create_engine
from sqlalchemy.exc import OperationalError
from sqlalchemy.orm.exc import NoResultFound
from ledd import VERSION
from ledd.controller import Controller
from ledd.effectstack import EffectStack
from ledd.models import Meta
from ledd.stripe import Stripe
from ledd.controller import Controller
from . import Base, session
log = logging.getLogger(__name__)
@@ -64,7 +65,6 @@ def run():
if not check_db():
init_db()
log.debug(Controller.query.all())
logging.getLogger("asyncio").setLevel(log.getEffectiveLevel())
# Load to cache
@@ -94,6 +94,10 @@ def run():
loop.run_forever()
except (KeyboardInterrupt, SystemExit):
log.info("Exiting")
for c in controller:
c.close()
try:
os.remove("ledd.pid")
except FileNotFoundError:
@@ -138,6 +142,7 @@ def start_effect(**kwargs):
"""
Part of the Color API. Used to start a specific effect.
Required parameters: stripe IDs: sids; effect id: eid, effect options: eopt
:param kwargs:
"""
if "sids" not in kwargs or "eid" not in kwargs or "eopt" not in kwargs:
@@ -198,7 +203,14 @@ def set_color(**kwargs):
stripe = get_stripe(kwargs['sid'])
if stripe:
stripe.set_color(spectra.hsv(kwargs['hsv']['h'], kwargs['hsv']['s'], kwargs['hsv']['v']))
try:
stripe.set_color(spectra.hsv(kwargs['hsv']['h'], kwargs['hsv']['s'], kwargs['hsv']['v']))
except OSError as e:
if int(e) == errno.ECOMM:
log.warning("Communication error on I2C Bus")
return e
else:
raise
else:
log.warning("Stripe not found: id=%s", kwargs['sid'])
return JSONRPCError(-1003, "Stripeid not found")
@@ -241,7 +253,7 @@ def add_controller(**kwargs):
try:
ncontroller = Controller(channels=int(kwargs['channels']), i2c_device=int(kwargs['i2c_dev']),
address=kwargs['address'])
address=kwargs['address'], _pwm_freq=1526)
except OSError as e:
log.error("Error opening i2c device: %s (%s)", kwargs['i2c_dev'], e)
return JSONRPCError(-1004, "Error while opening i2c device", e)
@@ -270,7 +282,11 @@ def get_color(**kwargs):
log.warning("Stripe not found: id=%s", kwargs['sid'])
return JSONRPCError(-1003, "Stripeid not found")
return {'color': stripe.color.values}
if stripe.color:
return {'color': stripe.color.values}
else:
log.warning("Stripe has no color: id=%s", kwargs['sid'])
return JSONRPCError(-1009, "Internal Error")
@dispatcher.add_method
@@ -331,7 +347,10 @@ def test_channel(**kwargs):
""" :type : ledd.controller.Controller """
if contr is not None:
contr.set_channel(kwargs['channel'], kwargs['value'], 2.8)
try:
contr.set_channel(kwargs['channel'], kwargs['value'], 2.8)
except OSError as e:
return JSONRPCError(-1009, "Internal Error", e)
else:
return JSONRPCError(-1002, "Controller not found")
@@ -373,16 +392,17 @@ class LedDProtocol(asyncio.Protocol):
except UnicodeDecodeError:
log.warning("Recieved undecodable data, ignoring")
else:
log.debug("Received: %s from: %s", d_decoded, self.transport.get_extra_info("peername"))
self.select_task(d_decoded)
def select_task(self, data):
if data:
data_split = data.splitlines()
log.debug(data_split)
for line in data_split:
if line:
self.transport.write(JSONRPCResponseManager.handle(line, dispatcher).json.encode())
try:
self.transport.write(JSONRPCResponseManager.handle(line, dispatcher).json.encode())
except TypeError as te:
log.warning("Can't send response: %s", te)
def connection_lost(self, exc):
log.info("Lost connection to %s", self.transport.get_extra_info("peername"))

View File

@@ -1,3 +1,19 @@
# LEDD Project
# Copyright (C) 2015 LEDD Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import asyncio
from ledd.effects.fadeeffect import FadeEffect