diff --git a/docs/images/apa102.jpg b/docs/images/apa102.jpg new file mode 100644 index 00000000..43395e36 Binary files /dev/null and b/docs/images/apa102.jpg differ diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index 483695c6..0d5475f0 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -250,6 +250,7 @@ add_example (si7005) add_example (t6713) add_example (cwlsxxa) add_example (teams) +add_example (apa102) # These are special cases where you specify example binary, source file and module(s) include_directories (${PROJECT_SOURCE_DIR}/src) diff --git a/examples/c++/apa102.cxx b/examples/c++/apa102.cxx new file mode 100644 index 00000000..78ee6bda --- /dev/null +++ b/examples/c++/apa102.cxx @@ -0,0 +1,51 @@ +/* + * Author: Yannick Adam + * Copyright (c) 2016 Yannick Adam + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "apa102.h" +#include +#include +#include + +using namespace std; + +int +main(int argc, char** argv) +{ + //! [Interesting] + // Instantiate a strip of 30 LEDs on SPI bus 0 + upm::APA102* ledStrip = new upm::APA102(800, 0); + + // Set all LEDs to Red + ledStrip->setAllLeds(31, 255, 0, 0); + + // Set a section (10 to 20) to blue + ledStrip->setLeds(10, 20, 31, 0, 0, 255); + + // Set a single LED to green + ledStrip->setLed(15, 31, 0, 255, 0); + + delete ledStrip; + //! [Interesting] + return 0; +} diff --git a/examples/java/APA102Sample.java b/examples/java/APA102Sample.java new file mode 100644 index 00000000..4ebbdbff --- /dev/null +++ b/examples/java/APA102Sample.java @@ -0,0 +1,44 @@ +/* + * Author: Yannick Adam + * Copyright (c) 2016 Yannick Adam + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +public class APA102Sample { + + public static void main(String[] args) throws InterruptedException { + // ! [Interesting] + // Instantiate a strip of 30 LEDs on SPI bus 0 + upm_apa102.APA102 ledStrip = + new upm_apa102.APA102(30, (short)0, false, (byte)-1); + + System.out.println("Set all LEDs to blue"); + ledStrip.setAllLeds((short)31, (short)0, (short)0, (short)255); + + System.out.println("Set LEDs between 10 and 20 to green"); + ledStrip.setLeds(10, 20, (short)31, (short)0, (short)255, (short)0); + + System.out.println("Set a single LED to red at index 15"); + ledStrip.setLed(15, (short)31, (short)255, (short)0, (short)0); + + // ! [Interesting] + } +} diff --git a/examples/java/CMakeLists.txt b/examples/java/CMakeLists.txt index 6e8d0537..e56b77ef 100644 --- a/examples/java/CMakeLists.txt +++ b/examples/java/CMakeLists.txt @@ -107,7 +107,7 @@ add_example(Th02Example th02) add_example(FlexSensorExample flex) add_example(CWLSXXA_Example cwlsxxa) add_example(TEAMS_Example teams) - +add_example(APA102Sample apa102) add_example_with_path(Jhd1313m1_lcdSample lcd/upm_i2clcd.jar) add_example_with_path(Jhd1313m1Sample lcd/upm_i2clcd.jar) diff --git a/examples/javascript/apa102.js b/examples/javascript/apa102.js new file mode 100644 index 00000000..c8b841f7 --- /dev/null +++ b/examples/javascript/apa102.js @@ -0,0 +1,44 @@ +/*jslint node:true, vars:true, bitwise:true, unparam:true */ +/*jshint unused:true */ +/* +* Author: Yannick Adam +* Copyright (c) 2016 Yannick Adam +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +var lib = require('jsupm_apa102'); + +// Instantiate a strip of 30 LEDs on SPI Bus 0 +var ledStrip = new lib.APA102(30, 0); + +// Set all LEDs to blue +ledStrip.setAllLeds(31, 0, 0, 255); + +// Set a mid-section to red +ledStrip.setLeds(10,20, 31, 255, 0, 0); + +// Set a single led to green +ledStrip.setLed(15, 31, 0, 255, 0); + + +// Exit +ledStrip = null; +process.exit(0); diff --git a/examples/python/apa102.py b/examples/python/apa102.py new file mode 100644 index 00000000..721d4d42 --- /dev/null +++ b/examples/python/apa102.py @@ -0,0 +1,48 @@ +#!/usr/bin/python +# Author: Yannick Adam +# Copyright (c) 2016 Yannick Adam +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +import time, sys, signal, atexit +import pyupm_apa102 as mylib + +# Instantiate a strip of 30 LEDs on SPI bus 0 +ledStrip = mylib.APA102(30, 0, False) + +## Exit handlers ## +# This stops python from printing a stacktrace when you hit control-C +def SIGINTHandler(signum, frame): + raise SystemExit + +# Register exit handlers +signal.signal(signal.SIGINT, SIGINTHandler) + +print "Setting all LEDs to Green" +ledStrip.setAllLeds(31, 0, 255, 0) + +print "Setting LEDs between 10 and 20 to Red" +ledStrip.setLeds(10, 20, 31, 255, 0, 0) + +print "Setting LED 15 to Blue" +ledStrip.setLed(15, 31, 0, 0, 255) + + diff --git a/src/apa102/CMakeLists.txt b/src/apa102/CMakeLists.txt new file mode 100644 index 00000000..df7db9e0 --- /dev/null +++ b/src/apa102/CMakeLists.txt @@ -0,0 +1,5 @@ +set (libname "apa102") +set (libdescription "upm apa102 led strip spi output module") +set (module_src ${libname}.cxx) +set (module_h ${libname}.h) +upm_module_init() diff --git a/src/apa102/apa102.cxx b/src/apa102/apa102.cxx new file mode 100644 index 00000000..8564a9d5 --- /dev/null +++ b/src/apa102/apa102.cxx @@ -0,0 +1,166 @@ +/* + * Author: Yannick Adam + * Copyright (c) 2016 Yannick Adam + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "apa102.h" + +using namespace upm; + +APA102::APA102(uint16_t ledCount, uint8_t spiBus, bool batchMode, int8_t csn) + : m_ledCount(ledCount), m_batchMode(batchMode) +{ + mraa::Result res = mraa::SUCCESS; + m_leds = NULL; + + // Optional chip select pin + m_csnPinCtx = NULL; + if (csn > -1) { + m_csnPinCtx = new mraa::Gpio(csn); + res = m_csnPinCtx->dir(mraa::DIR_OUT); + if (res != mraa::SUCCESS) { + throw std::invalid_argument(std::string(__FUNCTION__) + + ": GPIO failed to set direction"); + } + } + + CSOff(); + // Initialize SPI + m_spi = new mraa::Spi(spiBus); + + // Initialize LED array + uint16_t endFrameLength = (m_ledCount + 15) / 16; // End frame should be (leds/2) bits + m_frameLength = endFrameLength + (m_ledCount + 1) * 4; + if ((m_leds = (uint8_t*) malloc(m_frameLength))) { + memset(m_leds, 0x00, m_frameLength - 4); // Clear state + memset(&m_leds[m_frameLength - endFrameLength], 0xFF, endFrameLength); // Frame End + + // Need to set the brightness to "0" for each Led + for (int i = 1; i <= m_ledCount; i++) { + m_leds[i * 4] = 224; + } + + } else { + throw std::runtime_error(std::string(__FUNCTION__) + + ": Failed to allocate memory for LED Strip"); + } +} + +APA102::~APA102() +{ + // Clear leds + if (m_leds) { + free(m_leds); + } + + // Clear SPI + if (m_spi) { + delete m_spi; + } + + // Clear GPIO + if (m_csnPinCtx) { + delete m_csnPinCtx; + } +} + + +void +APA102::setLed(uint16_t ledIdx, uint8_t brightness, uint8_t r, uint8_t g, uint8_t b) +{ + setLeds(ledIdx, ledIdx, brightness, r, g, b); +} + +void +APA102::setAllLeds(uint8_t brightness, uint8_t r, uint8_t g, uint8_t b) +{ + setLeds(0, m_ledCount - 1, brightness, r, g, b); +} + +void +APA102::setLeds(uint16_t startIdx, uint16_t endIdx, uint8_t brightness, uint8_t r, uint8_t g, uint8_t b) +{ + uint16_t s_idx = (startIdx + 1) * 4; + uint16_t e_idx = (endIdx + 1) * 4; + + for (uint16_t i = s_idx; i <= e_idx; i += 4) { + m_leds[i] = brightness | 224; + m_leds[i + 1] = b; + m_leds[i + 2] = g; + m_leds[i + 3] = r; + } + + if (!m_batchMode) { + pushState(); + } +} + +void +APA102::setLeds(uint16_t startIdx, uint16_t endIdx, uint8_t* colors) +{ + uint16_t s_idx = (startIdx + 1) * 4; + memcpy(&m_leds[s_idx], colors, (endIdx - startIdx + 1) * 4); + + if (!m_batchMode) { + pushState(); + } +} + +void +APA102::pushState(void) +{ + CSOn(); + m_spi->write(m_leds, m_frameLength); + CSOff(); +} + +/* + * ************** + * private area + * ************** + */ + +mraa::Result +APA102::CSOn() +{ + if (m_csnPinCtx) { + return m_csnPinCtx->write(HIGH); + } + + return mraa::ERROR_FEATURE_NOT_SUPPORTED; +} + +mraa::Result +APA102::CSOff() +{ + if (m_csnPinCtx) { + return m_csnPinCtx->write(LOW); + } + + return mraa::ERROR_FEATURE_NOT_SUPPORTED; +} diff --git a/src/apa102/apa102.h b/src/apa102/apa102.h new file mode 100644 index 00000000..bed3d0aa --- /dev/null +++ b/src/apa102/apa102.h @@ -0,0 +1,142 @@ +/* + * Author: Yannick Adam + * Copyright (c) 2016 Yannick Adam + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#pragma once + +#include +#include +#include + +#define HIGH 1 +#define LOW 0 + +namespace upm +{ +/** + * @brief APA102 RGB LED Strip driver library + * @defgroup apa102 libupm-apa102 + * @ingroup spi led + */ + +/** + * @library apa102 + * @sensor apa102 + * @comname APA102/DotStar LED Strip + * @type led + * @man adafruit + * @con spi + * + * @brief API for controlling APA102/DotStar RGB LED Strips + * + * APA102 LED Strips provide individually controllable LEDs through a SPI interface. + * For each LED, brightness (0-31) and RGB (0-255) values can be set. + * + * @image html apa102.jpg + */ +class APA102 +{ + public: + /** + * Instantiates a new APA102 LED Strip + * + * @param ledCount Number of APA102 leds in the strip + * @param spiBus SPI Bus number + * @param batchMode (optional) Immediatly write to SPI (false, default) or wait for a pushState + * call (true) + * @param csn (optional) Chip Select Pin + */ + APA102(uint16_t ledCount, uint8_t spiBus, bool batchMode = false, int8_t csn = -1); + + /** + * APA102 destructor + */ + ~APA102(); + + /** + * Change the color for a single led + * + * @param ledIdx Index of the LED in the strip (0 based) + * @param brightness Brightness value (0-31) + * @param r Red component (0-255) + * @param g Green component (0-255) + * @param b Blue component (0-255) + */ + void setLed(uint16_t ledIdx, uint8_t brightness, uint8_t r, uint8_t g, uint8_t b); + + /** + * Change the color for all leds + * + * @param brightness Brightness value (0-31) + * @param r Red component (0-255) + * @param g Green component (0-255) + * @param b Blue component (0-255) + */ + void setAllLeds(uint8_t brightness, uint8_t r, uint8_t g, uint8_t b); + + /** + * Change the color for a range of leds + * + * @param startIdx Start index of the range of LEDs in the strip (0 based) + * @param endIdx End index of the range of LEDs in the strip (0 based) + * @param brightness Brightness value (0-31) + * @param r Red component (0-255) + * @param g Green component (0-255) + * @param b Blue component (0-255) + */ + void + setLeds(uint16_t startIdx, uint16_t endIdx, uint8_t brightness, uint8_t r, uint8_t g, uint8_t b); + + /** + * (Advanced) Manually control the colors of a range of LEDS + * Best used to maximize performance + * + * @param startIdx Start index of the range of LEDs to update (0 based) + * @param endIdx End index of the range of LEDs to update (0 based) + * @param colors Pointer to an array of bytes. Each color is described as the following: + * B1: Brightness (224-255) B2: Blue (0-255) B3: Green (0-255) B4: Red + *(0-255) + * No check done on the boundaries + */ + void setLeds(uint16_t startIdx, uint16_t endIdx, uint8_t* colors); + + /** + * Outputs the current LED data to the SPI bus + * Note: Only required if batch mode is set to TRUE + * + */ + void pushState(); + + private: + mraa::Spi* m_spi; + mraa::Gpio* m_csnPinCtx; + + uint16_t m_ledCount; + uint8_t* m_leds; + uint16_t m_frameLength; + + bool m_batchMode; + + mraa::Result CSOn(); + mraa::Result CSOff(); +}; +} diff --git a/src/apa102/javaupm_apa102.i b/src/apa102/javaupm_apa102.i new file mode 100644 index 00000000..21456285 --- /dev/null +++ b/src/apa102/javaupm_apa102.i @@ -0,0 +1,30 @@ +%module javaupm_apa102 + +%include "../upm.i" +%include "typemaps.i" + +%typemap(jtype) (uint8_t *colors) "byte[]" +%typemap(jstype) (uint8_t *colors) "byte[]" +%typemap(jni) (uint8_t *colors) "jbyteArray" +%typemap(javain) (uint8_t *colors) "$javainput" + +%typemap(in) (uint8_t *colors) { + $1 = (uint8_t*)JCALL2(GetByteArrayElements, jenv, $input, NULL); +} + +%{ + #include "apa102.h" +%} + +%include "apa102.h" + +%pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("javaupm_apa102"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } +%} diff --git a/src/apa102/jsupm_apa102.i b/src/apa102/jsupm_apa102.i new file mode 100644 index 00000000..2437e141 --- /dev/null +++ b/src/apa102/jsupm_apa102.i @@ -0,0 +1,19 @@ +%module jsupm_apa102 + +%include "../upm.i" +%inline %{ + #include +%} + +%typemap(in) (uint8_t *colors) { + if (!node::Buffer::HasInstance($input)) { + SWIG_exception_fail(SWIG_ERROR, "Expected a node Buffer"); + } + $1 = (uint8_t*) node::Buffer::Data($input); +} + +%include "apa102.h" + +%{ + #include "apa102.h" +%} diff --git a/src/apa102/pyupm_apa102.i b/src/apa102/pyupm_apa102.i new file mode 100644 index 00000000..941a33f0 --- /dev/null +++ b/src/apa102/pyupm_apa102.i @@ -0,0 +1,25 @@ +// Include doxygen-generated documentation +%include "pyupm_doxy2swig.i" + +%module pyupm_apa102 + +%include "../upm.i" + +%feature("autodoc", "3"); + +// setLeds +%typemap(in) (uint8_t *colors) { + if (PyByteArray_Check($input)) { + $1 = (uint8_t*) PyByteArray_AsString($input); + } else { + PyErr_SetString(PyExc_ValueError, "bytearray expected"); + return NULL; + } +} + +%include "apa102.h" + +%{ + + #include "apa102.h" +%}