├── .gitignore ├── LICENSE ├── README.md ├── build-native-on-remote-pi.sh ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── install-swig-on-pi.sh ├── settings.gradle └── src ├── examples └── java │ └── com │ └── github │ └── mng │ └── ws281x │ └── examples │ ├── ConsecutiveTurnOnExample.java │ └── RainbowExample.java ├── main ├── java │ └── com │ │ └── github │ │ └── mbelling │ │ └── ws281x │ │ ├── Color.java │ │ ├── LedStrip.java │ │ ├── LedStripType.java │ │ └── Ws281xLedStrip.java └── resources │ └── log4j2.xml ├── scripts └── createNativeLib.sh ├── swig └── rpi_ws281x.i └── test └── java └── com └── github └── mbelling └── ws281x └── ColorTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Directories 2 | .gradle 3 | .idea 4 | .settings 5 | .vscode 6 | bin 7 | build 8 | libs 9 | out 10 | 11 | # Files 12 | .classpath 13 | .project 14 | *.iml 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rpi-ws281x-java 2 | rpi281x wrapper for Java using SWIG 3 | 4 | ## TL;DR 5 | 6 | Successfully tested on a RaspberryPi 3B+ 7 | 8 | 1. set RaspberryPi ssh username and password in gradle.properties 9 | 2. run `./gradlew installSwigOnPi` 10 | 3. run `./gradlew buildNativeOnPi` 11 | 4. run `./gradlew publishToMavenLocal -PtargetComp=11` 12 | 5. run `./gradlew runExample -PtargetComp=11 -PamountOfLeds=16 -Pexample=RainbowExample` to test.
13 | set `-PamountOfLeds` to the amount of LEDs on the strip.
14 | set `-Pexample` to the name of the example-class (in src/examples) you want to execute.
15 | set `-PtargetComp=11` if the java version on your raspi is 11 (often true because it's the default-jdk). Otherwise it is assumed that the java version on your raspi matches the one gradle uses. Only 11 can be passed currently. 16 | 7. kill runing example java app by ssh-ing into your raspi and using `htop` to send a `SIGINT` signal to the process. 17 | 18 | ## To build on a raspberry pi 19 | 20 | Run `src/scripts/createNativeLib.sh` to generate the SWIG java code and generate the libws2811.so native library (For a tutorial on how to install SWIG, see "Install SWIG on RaspberryPi" below). 21 | 22 | Run `./gradlew assemble` to compile the java code and create a jar containing the compile class files and the native .so file. 23 | 24 | ## To build from another machine 25 | 26 | Set the appropriate username, host and password (or path to private key) in `gradle.properties`. 27 | 28 | Then, run `./gradlew buildNativeOnPi`. Note that your RaspberryPi needs to have ssh installed as gradle will use *its* ssh client. 29 | 30 | Alternatively, you can also use the script `build-native-on-remote-pi.sh` with bash or a bash-compatible shell (like babun, cygwin, or git-bash on windows.). 31 | 32 | 33 | This will copy the project to the pi, and run the script in the previous section, and copy the .so library back to the dev machine. After that, run `.\gradlew assemble` to compile the java code and create a jar containing the compile class files and the native .so file. The easiest way to use the jar would be to publish it to a maven repository, or your local .m2 repository using `./gradlew publishToMavenLocal` and use maven coordinates in your maven or gradle project. 34 | 35 | ## To install SWIG on the RaspberryPi 36 | 37 | You can try `./gradlew installSwigOnPi` to automatically execute all the steps detailed below on your pi. 38 | 39 | **NOTE:** 40 | The default-jdk-headless which this task will try to install works only on pis with ARMv7+ architecture (check with the command `cat /proc/cpuinfo`). 41 | The script will try to detect this and install the zulu jdk as an alternative, but this has only been tested on a raspberrypi Zero so far. 42 | On my RasPi Zero this task took 30min the majority of which was spent building SWIG. 43 | On the RasPi 3 B it took 8 mins. 44 | 45 | You can read more in the [pi4j docs](https://pi4j.com/documentation/java-installation/). 46 | 47 | These commands worked for me: 48 | 1. Install [prereqs](https://github.com/swig/swig/wiki/Getting-Started#linux---ubuntu) 49 | ```shell 50 | sudo apt-get install build-essential libpcre2-dev libpcre3-dev 51 | ``` 52 | 2. Install default jdk 53 | ```shell 54 | sudo apt install default-jdk-headless 55 | ``` 56 | 57 | 3. Download swig tarball and unzip 58 | ```shell 59 | wget http://prdownloads.sourceforge.net/swig/swig-4.1.1.tar.gz 60 | tar -xzvf swig-4.1.1.tar.gz 61 | ``` 62 | 4. Enter directory and start building 63 | ```shell 64 | cd swig-4.1.1 65 | ./configure && make 66 | ``` 67 | 5. Execute an example as a check 68 | ```shell 69 | cd Examples/java/class && make check 70 | ``` 71 | 6. Install SWIG 72 | ```shell 73 | cd ../../../ 74 | sudo make install 75 | swig -version 76 | ``` 77 | ``` 78 | SWIG Version 4.1.1 79 | 80 | Compiled with g++ [x86_64-pc-linux-gnu] 81 | 82 | Configured options: +pcre 83 | 84 | Please see https://www.swig.org for reporting bugs and further information 85 | ``` 86 | 87 | For reference, the [SWIG DOCS](https://www.swig.org/Doc4.1/SWIGDocumentation.html#Preface_unix_installation). 88 | 89 | Attribution 90 | ----------- 91 | * SWIG generation and native lib compiling based on scripts at https://github.com/limpygnome/build-tv 92 | * build-native-on-remote-pi script based on script at https://github.com/Cacodaimon/rpi_ws281x4j 93 | * Loading JNI native .so file code based on code at https://github.com/mattjlewis/diozero/blob/master/diozero-ws281x-java 94 | -------------------------------------------------------------------------------- /build-native-on-remote-pi.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -z "$1" ]; then 4 | echo "Please provide the Raspberry PI location and username as first parameter." 5 | echo "For example: $0 pi@192.168.1.111" 6 | exit 7 | fi 8 | 9 | RASPBERRY_PI="$1" 10 | REMOTE_BUILD_DIR="/tmp/rpi_ws281x_build" 11 | 12 | echo "Creating target dir in /tmp" 13 | ssh "$RASPBERRY_PI" -t "mkdir $REMOTE_BUILD_DIR" 14 | 15 | echo "Copy necessary files" 16 | rsync -a ./* "$RASPBERRY_PI":$REMOTE_BUILD_DIR --delete --exclude build --exclude copy-to-rpi.sh 17 | 18 | echo "Run build.sh on Raspberry PI" 19 | ssh "$RASPBERRY_PI" -t "cd $REMOTE_BUILD_DIR; bash src/scripts/createNativeLib.sh" 20 | 21 | echo "Copy rpiWs821xJava.jar back to development PC" 22 | scp -r "$RASPBERRY_PI":$REMOTE_BUILD_DIR/build . 23 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'net.researchgate.release' version '2.6.0' 3 | id 'org.hidetake.ssh' version '2.11.2' 4 | id 'java' 5 | id 'maven-publish' 6 | id 'signing' 7 | } 8 | 9 | def whichTarget = project.findProperty("targetComp"); 10 | 11 | java { 12 | if (whichTarget == "11") { 13 | println("SETTING TARGET & SOURCE COMPATIBILITY JAVA VERSION TO 11!!!") 14 | setSourceCompatibility JavaVersion.VERSION_11 15 | setTargetCompatibility JavaVersion.VERSION_11 16 | } else { 17 | setSourceCompatibility JavaVersion.VERSION_17 18 | } 19 | withJavadocJar() 20 | withSourcesJar() 21 | } 22 | 23 | remotes { 24 | rasPi { 25 | host = project.findProperty("RasPiHost") 26 | user = project.findProperty("RasPiUser") 27 | if (project.hasProperty("RasPiKey")) { 28 | identity = file(project.findProperty("RasPiKey")) 29 | } else { 30 | password = project.findProperty("RasPiPassword") 31 | } 32 | fileTransfer = "scp" 33 | jschLog = true 34 | logging = 'slf4j' 35 | } 36 | } 37 | 38 | group = 'com.github.mbelling' 39 | //flag to disable signing if keyInfo ain't present 40 | boolean doSigning = project.hasProperty('signing.keyId') 41 | && project.hasProperty('signing.password') 42 | && project.hasProperty('signing.secretKeyRingFile'); 43 | 44 | sourceSets { 45 | main { 46 | java { 47 | // Get the generated source from SWIG 48 | srcDirs('build/generatedSource/java') 49 | } 50 | resources { 51 | // Get the native .so file 52 | srcDirs('build/nativeLib/') 53 | } 54 | } 55 | examples { 56 | java 57 | } 58 | } 59 | 60 | jar { 61 | from sourceSets.main.output 62 | } 63 | 64 | repositories { 65 | mavenLocal() 66 | mavenCentral() 67 | } 68 | 69 | dependencies { 70 | implementation 'org.slf4j:slf4j-api:1.7.32' 71 | 72 | testImplementation 'junit:junit:4.13.1' 73 | 74 | //examplesImplementation 'com.github.mbelling:rpi-ws281x-java:2.0.0-SNAPSHOT' //doesn't work for some reason 🙄 75 | examplesImplementation files("${System.properties['user.home']}/.m2/repository/com/github/mbelling/rpi-ws281x-java/2.0.0-SNAPSHOT/rpi-ws281x-java-2.0.0-SNAPSHOT.jar") 76 | examplesImplementation 'org.slf4j:slf4j-api:1.7.32' 77 | examplesImplementation 'org.slf4j:slf4j-simple:1.7.32' 78 | } 79 | 80 | tasks.register('installSwigOnPi') { 81 | description = 'try to install swig on the raspi' 82 | def swigDir = "~/rpi_ws281x_build/swigInstall" 83 | doLast { 84 | ssh.run { 85 | session(remotes.rasPi) { 86 | println("ATEMPTING TO INSTALL SWIG") 87 | execute "mkdir -p $swigDir" 88 | put(from: 'install-swig-on-pi.sh', into: swigDir) 89 | execute "cd $swigDir; chmod +x install-swig-on-pi.sh" 90 | execute("cd $swigDir; bash install-swig-on-pi.sh 1>&2") //re-route stdout to stderr so the user sees all the log output 91 | println("SUCCESS!") 92 | } 93 | } 94 | } 95 | } 96 | 97 | def remoteBuildDir = project.findProperty("RasPiRemoteBuildDir") 98 | tasks.register('buildNativeOnPi') { 99 | description = 'build native lib on remote pi' 100 | def preamble = "[buildNativeOnPi] -" 101 | def scriptName = "src/scripts/createNativeLib.sh" 102 | doLast { 103 | ssh.run { 104 | session(remotes.rasPi) { 105 | println("$preamble make build dir in case it doesn't exists") 106 | execute "mkdir -p $remoteBuildDir" 107 | println("$preamble Copy all files...") 108 | put from: 'src/', into: "$remoteBuildDir/" 109 | 110 | //remove windows line breaks 111 | //execute "cd $remoteBuildDir; tr -d '\\r' < scripts/createNativeLib.sh > scripts/createNativeLib.sh" 112 | execute "cd $remoteBuildDir; chmod +x $scriptName" 113 | //now build 114 | println("$preamble Execute build script...") 115 | execute("cd $remoteBuildDir; bash $scriptName 1>&2") //re-route stdout to stderr so the user sees all the log output 116 | println("$preamble Copy generated artifacts back") 117 | get from: "$remoteBuildDir/build", into: '.' 118 | 119 | println("$preamble All Done! Try ./gradlew assemble") 120 | } 121 | } 122 | 123 | } 124 | } 125 | tasks.register('uberExamplesJar', Jar) { 126 | archiveClassifier = 'uberExamples' 127 | def exeClass = project.findProperty("example") 128 | manifest { 129 | attributes 'Main-Class': "com.github.mng.ws281x.examples.$exeClass" 130 | } 131 | from sourceSets.examples.output 132 | dependsOn configurations.examplesRuntimeClasspath 133 | from { 134 | configurations.examplesRuntimeClasspath.findAll({ it.name.endsWith("jar") }).collect { zipTree(it) } 135 | } 136 | } 137 | 138 | def amountOfLeds = project.findProperty("amountOfLeds") 139 | tasks.register("runExample"){ 140 | doLast { 141 | ssh.run { 142 | session(remotes.rasPi) { 143 | put from: './build/libs/rpi-ws281x-java-2.0.0-SNAPSHOT-uberExamples.jar', into: remoteBuildDir 144 | executeSudo "java -jar $remoteBuildDir/rpi-ws281x-java-2.0.0-SNAPSHOT-uberExamples.jar $amountOfLeds 1>&2" //re-route stdout to stderr so the user sees all the log output 145 | 146 | } 147 | } 148 | } 149 | } 150 | 151 | runExample.dependsOn uberExamplesJar 152 | 153 | tasks.register('checkSharedLibrary') { 154 | description = "Check if the shared library exists in the desired location" 155 | 156 | doLast { 157 | def libraryFile = file('build/nativeLib/libws281x.so') 158 | 159 | if (!libraryFile.exists()) { 160 | throw new GradleException("Shared library is missing in the desired location. Consult README and try ./gradlew buildNativeOnPi") 161 | } 162 | } 163 | } 164 | 165 | compileJava.dependsOn checkSharedLibrary 166 | 167 | publishing { 168 | publications { 169 | mavenJava(MavenPublication) { 170 | customizePom(pom) 171 | groupId project.group 172 | artifactId project.name 173 | version project.version 174 | from components.java 175 | pom { 176 | withXml { 177 | def pomFile = file("${project.buildDir}/generated-pom.xml") 178 | writeTo(pomFile) 179 | if (doSigning) { 180 | def pomAscFile = signing.sign(pomFile).signatureFiles[0] 181 | artifact(pomAscFile) { 182 | classifier = null 183 | extension = 'pom.asc' 184 | } 185 | } 186 | } 187 | } 188 | 189 | if (doSigning) { 190 | // create the signed artifacts 191 | project.tasks.signArchives.signatureFiles.each { 192 | artifact(it) { 193 | def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/ 194 | if (matcher.find()) { 195 | classifier = matcher.group(1) 196 | } else { 197 | classifier = null 198 | } 199 | extension = 'jar.asc' 200 | } 201 | } 202 | } 203 | } 204 | } 205 | repositories { 206 | maven { 207 | url "https://oss.sonatype.org/content/repositories/snapshots" 208 | credentials { 209 | username sonatypeUsername 210 | password sonatypePassword 211 | } 212 | } 213 | } 214 | } 215 | 216 | def customizePom(pom) { 217 | pom.withXml { 218 | def root = asNode() 219 | // eliminate test-scoped dependencies (no need in maven central POMs) 220 | root.dependencies.removeAll { dep -> 221 | dep.scope == "test" 222 | } 223 | // add all items necessary for maven central publication 224 | root.children().last() + { 225 | resolveStrategy = Closure.DELEGATE_FIRST 226 | description 'Java wrapper for rpi_ws281x library using SWIG' 227 | name 'rpi-ws281x-java' 228 | url 'https://github.com/rpi-ws281x/rpi-ws281x-java' 229 | organization { 230 | name 'rpi-ws281x' 231 | url 'https://github.com/rpi-ws281x/' 232 | } 233 | issueManagement { 234 | system 'GitHub' 235 | url 'https://github.com/rpi-ws281x/rpi-ws281x-java/issues' 236 | } 237 | licenses { 238 | license { 239 | name 'Apache License 2.0' 240 | url 'https://github.com/rpi-ws281x/rpi-ws281x-java/blob/master/LICENSE' 241 | distribution 'repo' 242 | } 243 | } 244 | scm { 245 | url 'https://github.com/rpi-ws281x/rpi-ws281x-java' 246 | connection 'scm:git:git://github.com/rpi-ws281x/rpi-ws281x-java.git' 247 | developerConnection 'scm:git:ssh://git@github.com:rpi-ws281x/rpi-ws281x-java.git' 248 | } 249 | developers { 250 | developer { 251 | name "Matthew Bellinger" 252 | } 253 | developer { 254 | name "MNG" 255 | roles { 256 | role "Update and Improvements like added checks, examples and increased usability" 257 | } 258 | } 259 | } 260 | } 261 | } 262 | } 263 | 264 | // Check if signing properties are defined in gradle.properties 265 | if (doSigning) { 266 | signing { 267 | sign configurations.archives 268 | } 269 | 270 | tasks.named 'publishMavenJavaPublicationToMavenLocal' { 271 | dependsOn project.tasks.named 'signArchives' 272 | } 273 | tasks.named 'publishMavenJavaPublicationToMavenRepository' { 274 | dependsOn project.tasks.named 'signArchives' 275 | } 276 | } 277 | 278 | model { 279 | tasks.generatePomFileForMavenJavaPublication { 280 | destination = layout.buildDirectory.file("generated-pom.xml") 281 | } 282 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=2.0.0-SNAPSHOT 2 | 3 | # Dummy values so the build does not break 4 | sonatypeUsername=empty 5 | sonatypePassword=empty 6 | #Raspi remote build properties 7 | #example 8 | #ssh pi@raspi 9 | #pi's pw: hithere 10 | #if you want to use a key instead of a password, uncomment the RasPiKey property 11 | RasPiUser=pi 12 | RasPiHost=raspi 13 | RasPiPassword=hithere 14 | #RasPiKey=C:\\Path\\To\\Private\\SSH\\key for windows or /path/to/private/ssh/key for unix 15 | RasPiRemoteBuildDir=~/rpi_ws281x_build 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rpi-ws281x/rpi-ws281x-java/d101c645ca2704dfbe2ad045250980168bb40043/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /install-swig-on-pi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -o pipefail 4 | cd ~ 5 | sudo apt-get --yes --force-yes install build-essential libpcre2-dev libpcre3-dev 6 | set +e 7 | echo "check if java is already installed..." 8 | java --version | grep jdk 9 | if [ $? -ne 0 ] 10 | then 11 | set -e 12 | echo "didn't detect install, will try..." 13 | echo "check cpu architecture..." 14 | cat /proc/cpuinfo | grep ARMv6 15 | if [ $? -eq 0 ] 16 | then 17 | echo "DETECTED ARMv6, WILL TRY AND INSTALL ZULU JDK!" 18 | mkdir -p /usr/lib/jvm 19 | cd /usr/lib/jvm 20 | sudo wget https://cdn.azul.com/zulu-embedded/bin/zulu11.41.75-ca-jdk11.0.8-linux_aarch32hf.tar.gz 21 | sudo tar -xzvf zulu11.41.75-ca-jdk11.0.8-linux_aarch32hf.tar.gz 22 | sudo rm zulu11.41.75-ca-jdk11.0.8-linux_aarch32hf.tar.gz 23 | sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/zulu11.41.75-ca-jdk11.0.8-linux_aarch32hf/bin/java 1 24 | sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/zulu11.41.75-ca-jdk11.0.8-linux_aarch32hf/bin/javac 1 25 | else 26 | echo "didn't detect ARMv6, will try and install default jdk..." 27 | sudo apt-get --yes --force-yes install default-jdk-headless 28 | fi 29 | else 30 | echo "detected java, skipping install" 31 | fi 32 | set -e 33 | mkdir -p ~/rpi_ws281x_build/swigInstall 34 | cd ~/rpi_ws281x_build/swigInstall 35 | if [ -d "swig-4.1.1" ]; then 36 | echo "found swig folder, skipping download..." 37 | else 38 | echo "attempting swig 4.1.1 download" 39 | wget http://prdownloads.sourceforge.net/swig/swig-4.1.1.tar.gz 40 | tar -xzvf swig-4.1.1.tar.gz 41 | rm swig-4.1.1.tar.gz 42 | fi 43 | 44 | cd swig-4.1.1 45 | echo "trying to figure out if --with-javainc arg needs/can to be passed to swig configure..." 46 | echo "executing part of swig configure script to determine if it will find jni.h..." 47 | 48 | JAVAINCDIR="/usr/j2sdk*/include /usr/local/j2sdk*/include /usr/jdk*/include /usr/local/jdk*/include /opt/j2sdk*/include /opt/jdk*/include /usr/java/include /usr/java/j2sdk*/include /usr/java/jdk*/include /usr/local/java/include /opt/java/include /usr/include/java /usr/local/include/java /usr/lib/java/include /usr/lib/jvm/java*/include /usr/lib64/jvm/java*/include /usr/include/kaffe /usr/local/include/kaffe /usr/include" 49 | 50 | 51 | for d in $JAVAINCDIR ; do 52 | if test -r "$d/jni.h" ; then 53 | echo "yep, found something, setting flag" 54 | FOUNDJNI="true" 55 | break 56 | fi 57 | done 58 | 59 | if [ "$FOUNDJNI" != "true" ]; then 60 | echo "configure won't find anything, will try to get a path for JNI.h" 61 | for d2 in /usr/lib/jvm/*/include ; do 62 | if test -r "$d2/jni.h" ; then 63 | echo "SUCCESS! found $d2" 64 | echo "will pass to configure" 65 | SELFJNI=$d2 66 | break 67 | fi 68 | done 69 | fi 70 | 71 | 72 | 73 | 74 | if [ "$FOUNDJNI" = "true" ]; then 75 | sudo ./configure 76 | else 77 | if [ -z ${SELFJNI+x} ]; then 78 | echo "*********************[install-swig-on-pi.sh]*********************" 79 | echo "NO JNI FOUND! NO POINT IN COMPILING SWIG ONLY FOR IT TO FAIL!" 80 | echo "you could try running ./configure in $(pwd) yourself. If at the end it outputs:" 81 | echo "'The SWIG test-suite and examples are configured for the following languages:'" 82 | echo " java perl5" 83 | echo "...then you're fine. You can then run the very time consuming command 'make' to compile swig." 84 | echo "*****************************************************************" 85 | 86 | exit 1 87 | else 88 | echo "passing include dir '$SELFJNI' to ./configure..." 89 | sudo ./configure --with-javaincl=$SELFJNI 90 | fi 91 | fi 92 | sudo make 93 | echo "*********************[install-swig-on-pi.sh]*********************" 94 | echo "COMPILATION DONE!!" 95 | echo "*****************************************************************" 96 | cd Examples/java/class && make check 97 | echo "*********************[install-swig-on-pi.sh]*********************" 98 | echo "CHECK DONE!!" 99 | echo "*****************************************************************" 100 | cd ../../../ 101 | sudo make install 102 | set +e 103 | source ~/.bashrc 104 | /usr/local/bin/swig -version | grep "SWIG Version 4.1.1" 105 | if [ $? -ne 0 ]; then 106 | echo "*********************[install-swig-on-pi.sh]*********************" 107 | echo "swig -version command didn't output expected info. Install probably failed 😣" 108 | echo "*****************************************************************" 109 | 110 | exit 1; 111 | else 112 | echo "*********************[install-swig-on-pi.sh]*********************" 113 | echo "Install worked!!! 🔥🔥😀" 114 | echo "*****************************************************************" 115 | 116 | exit 0; 117 | fi -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | 2 | rootProject.name = 'rpi-ws281x-java' 3 | -------------------------------------------------------------------------------- /src/examples/java/com/github/mng/ws281x/examples/ConsecutiveTurnOnExample.java: -------------------------------------------------------------------------------- 1 | package com.github.mng.ws281x.examples; 2 | 3 | 4 | import com.github.mbelling.ws281x.Color; 5 | import com.github.mbelling.ws281x.LedStripType; 6 | import com.github.mbelling.ws281x.Ws281xLedStrip; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public class ConsecutiveTurnOnExample { 11 | private static Logger log = LoggerFactory.getLogger(ConsecutiveTurnOnExample.class); 12 | 13 | public static void main(String[] args) throws InterruptedException { 14 | log.info("STARTING CONSECUTIVE TURN-ON EXAMPLE"); 15 | log.info("received args: {}", (Object[]) args); 16 | int amount = Integer.parseInt(args[0]); 17 | var strip = new Ws281xLedStrip(amount, 10, 800000, 0, 255, 0, false, LedStripType.WS2811_STRIP_GRB, true); 18 | int curr = 0; 19 | while (!Thread.interrupted()) { 20 | Color col; 21 | switch (curr % 3) { 22 | case 0: 23 | col = Color.RED; 24 | break; 25 | case 1: 26 | col = Color.GREEN; 27 | break; 28 | default: 29 | col = Color.BLUE; 30 | break; 31 | } 32 | strip.setPixel(curr++, col); 33 | if (curr > amount) { 34 | curr = 0; 35 | strip.setStrip(Color.BLACK); 36 | } 37 | log.info("rendered {}", curr); 38 | strip.render(); 39 | Thread.sleep(1000); 40 | } 41 | log.info("EXITING CONSECUTIVE TURN-ON EXAMPLE BYE!"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/examples/java/com/github/mng/ws281x/examples/RainbowExample.java: -------------------------------------------------------------------------------- 1 | package com.github.mng.ws281x.examples; 2 | 3 | 4 | import com.github.mbelling.ws281x.Color; 5 | import com.github.mbelling.ws281x.LedStrip; 6 | import com.github.mbelling.ws281x.LedStripType; 7 | import com.github.mbelling.ws281x.Ws281xLedStrip; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | public class RainbowExample { 12 | private static Logger log = LoggerFactory.getLogger(RainbowExample.class); 13 | 14 | //Generate rainbow colors across 0-255 positions. 15 | // taken from strandtest.py from the python rpi-ws281x project 16 | private static Color wheel(int pos) { 17 | if (pos < 85) { 18 | return new Color(pos * 3, 255 - pos * 3, 0); 19 | } else if (pos < 170) { 20 | pos -= 85; 21 | return new Color(255 - pos * 3, 0, pos * 3); 22 | } else { 23 | pos -= 170; 24 | return new Color(0, pos * 3, 255 - pos * 3); 25 | } 26 | } 27 | 28 | //Draw rainbow that fades across all pixels at once. 29 | // taken from strandtest.py from the python rpi-ws281x project 30 | private static void rainbow(LedStrip strip, int numPixels) throws InterruptedException { 31 | final int wait_ms = 20; 32 | final int iterations = 1; 33 | for (int j = 0; j < 256 * iterations; j++) { 34 | for (int i = 0; i < numPixels; i++) { 35 | strip.setPixel(i, wheel((i + j) & 255)); 36 | } 37 | strip.render(); 38 | Thread.sleep(wait_ms); 39 | } 40 | } 41 | 42 | public static void main(String[] args) throws InterruptedException { 43 | log.info("STARTING RAINBOW EXAMPLE"); 44 | log.info("received args: {}", (Object[]) args); 45 | int amount = Integer.parseInt(args[0]); 46 | var strip = new Ws281xLedStrip(amount, 10, 800000, 0, 255, 0, false, LedStripType.WS2811_STRIP_GRB, true); 47 | while (!Thread.interrupted()) { 48 | rainbow(strip, amount); 49 | } 50 | log.info("EXITING CONSECUTIVE TURN-ON EXAMPLE BYE!"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/mbelling/ws281x/Color.java: -------------------------------------------------------------------------------- 1 | package com.github.mbelling.ws281x; 2 | 3 | public class Color { 4 | private final int red; 5 | private final int green; 6 | private final int blue; 7 | 8 | public Color( long color ) { 9 | this.red = (short) (color >> 16) & 255; 10 | this.green = (short) (color >> 8) & 255; 11 | this.blue = (short) (color) & 255; 12 | } 13 | 14 | public Color( int red, int green, int blue ) { 15 | this.red = red; 16 | this.green = green; 17 | this.blue = blue; 18 | } 19 | 20 | public int getRed() { 21 | return red; 22 | } 23 | 24 | public int getGreen() { 25 | return green; 26 | } 27 | 28 | public int getBlue() { 29 | return blue; 30 | } 31 | 32 | public long getColorBits() { 33 | return buildColour(red, green, blue); 34 | } 35 | 36 | public static long buildColour( int red, int green, int blue ) { 37 | return ( (short) red << 16 ) | ( (short) green << 8 ) | (short) blue; 38 | } 39 | 40 | // Predefined colors 41 | public final static Color WHITE = new Color(255, 255, 255); 42 | public final static Color LIGHT_GRAY = new Color(192, 192, 192); 43 | public final static Color GRAY = new Color(128, 128, 128); 44 | public final static Color DARK_GRAY = new Color(64, 64, 64); 45 | public final static Color BLACK = new Color(0, 0, 0); 46 | public final static Color RED = new Color(255, 0, 0); 47 | public final static Color PINK = new Color(255, 175, 175); 48 | public final static Color ORANGE = new Color(255, 200, 0); 49 | public final static Color YELLOW = new Color(255, 255, 0); 50 | public final static Color GREEN = new Color(0, 255, 0); 51 | public final static Color MAGENTA = new Color(255, 0, 255); 52 | public final static Color CYAN = new Color(0, 255, 255); 53 | public final static Color BLUE = new Color(0, 0, 255); 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/mbelling/ws281x/LedStrip.java: -------------------------------------------------------------------------------- 1 | package com.github.mbelling.ws281x; 2 | 3 | public interface LedStrip { 4 | 5 | /** 6 | * Set the color of an individual pixel 7 | * 8 | * @param pixel The index of the pixel in the strip 9 | * @param red The red value (0 - 255) 10 | * @param green The green value (0 - 255) 11 | * @param blue The blue value (0 - 255) 12 | */ 13 | void setPixel( int pixel, int red, int green, int blue ); 14 | 15 | /** 16 | * Set the color of an individual pixel 17 | * 18 | * @param pixel The index of the pixel in the strip 19 | * @param color The color to set on the pixel 20 | */ 21 | void setPixel( int pixel, Color color ); 22 | 23 | /** 24 | * Set all LEDs in the strip to one color 25 | * 26 | * @param red The red value (0 - 255) 27 | * @param green The green value (0 - 255) 28 | * @param blue The blue value (0 - 255) 29 | */ 30 | void setStrip( int red, int green, int blue ); 31 | 32 | /** 33 | * Set all LEDs in the strip to one color 34 | * 35 | * @param color The color to set on the strip 36 | */ 37 | void setStrip( Color color ); 38 | 39 | /** 40 | * Render the current values to the physical light strip. 41 | * 42 | * This method needs to be called after any previous setPixel calls to make the lights change to those colors. 43 | */ 44 | void render(); 45 | 46 | /** 47 | * Set the brightness value from 0-255 48 | * @param brightness The brightnes to set the strip to 49 | */ 50 | void setBrightness( int brightness ); 51 | 52 | /** 53 | * Get the current brightness value of the strip 54 | * @return The current brightness value 55 | */ 56 | int getBrightness(); 57 | 58 | /** 59 | * Get the number of LEDs in this strip 60 | * @return The number of LEDs 61 | */ 62 | int getLedsCount(); 63 | 64 | /** 65 | * Get the color of a pixel 66 | * 67 | * @return The color of the pixel as a long 68 | */ 69 | long getPixel( int pixel ); 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/github/mbelling/ws281x/LedStripType.java: -------------------------------------------------------------------------------- 1 | package com.github.mbelling.ws281x; 2 | 3 | /** 4 | * Dependent on constants defined in original rpi_ws281x repository (https://github.com/jgarff/rpi_ws281x). 5 | */ 6 | public enum LedStripType { 7 | SK6812_STRIP_RGBW, 8 | SK6812_STRIP_RBGW, 9 | SK6812_STRIP_GRBW, 10 | SK6812_STRIP_GBRW, 11 | SK6812_STRIP_BRGW, 12 | SK6812_STRIP_BGRW, 13 | WS2811_STRIP_RGB, 14 | WS2811_STRIP_RBG, 15 | WS2811_STRIP_GRB, 16 | WS2811_STRIP_GBR, 17 | WS2811_STRIP_BRG, 18 | WS2811_STRIP_BGR 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/mbelling/ws281x/Ws281xLedStrip.java: -------------------------------------------------------------------------------- 1 | package com.github.mbelling.ws281x; 2 | 3 | import com.github.mbelling.ws281x.jni.*; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.nio.file.StandardCopyOption; 11 | import java.util.concurrent.atomic.AtomicBoolean; 12 | 13 | import static com.github.mbelling.ws281x.Color.buildColour; 14 | 15 | /** 16 | * Basic class to interface with the rpi_ws281x native library 17 | */ 18 | public class Ws281xLedStrip implements LedStrip { 19 | private static final Logger LOG = LoggerFactory.getLogger( Ws281xLedStrip.class ); 20 | private static final String LIB_NAME = "ws281x"; 21 | 22 | private static final AtomicBoolean loaded = new AtomicBoolean( false ); 23 | 24 | // Settings 25 | private int ledsCount; 26 | private int gpioPin; 27 | private int frequencyHz; 28 | private int dma; 29 | private int brightness; 30 | private int pwmChannel; 31 | private boolean invert; 32 | private LedStripType stripType; 33 | private boolean clearOnExit; 34 | 35 | private ws2811_t leds; 36 | private ws2811_channel_t currentChannel; 37 | 38 | /** 39 | * Default constructor with the following settings: 40 | * 50 | * 51 | * @see rpi_ws281xConstants 52 | * @see #Ws281xLedStrip(int, int, int, int, int, int, boolean, LedStripType, boolean) 53 | */ 54 | public Ws281xLedStrip() { 55 | this( 56 | 100, // leds 57 | 10, // Using pin 10 to do SPI, which should allow non-sudo access 58 | 800000, // freq hz 59 | 10, // dma 60 | 255, // brightness 61 | 0, // pwm channel 62 | false, // invert 63 | LedStripType.WS2811_STRIP_RGB, // Strip type 64 | false // clear on exit 65 | ); 66 | } 67 | 68 | /** 69 | * Create an LED strip with the given settings 70 | * 71 | * @param ledsCount The number of LEDs in the strip 72 | * @param gpioPin The Raspberry Pi GPIO pin 73 | * @param frequencyHz Frequency of updates in Hertz 74 | * @param dma DMA to use 75 | * @param brightness Starting brightness for colors 76 | * @param pwmChannel PWM Channel to use 77 | * @param invert Whether or not to invert color values 78 | * @param stripType The type of LED Strip {@link com.github.mbelling.ws281x.jni.rpi_ws281xConstants} 79 | * @param clearOnExit Clear LEDs on exit 80 | */ 81 | public Ws281xLedStrip( int ledsCount, int gpioPin, int frequencyHz, int dma, int brightness, int pwmChannel, 82 | boolean invert, LedStripType stripType, boolean clearOnExit ) { 83 | this.ledsCount = ledsCount; 84 | this.gpioPin = gpioPin; 85 | this.frequencyHz = frequencyHz; 86 | this.dma = dma; 87 | this.brightness = brightness; 88 | this.pwmChannel = pwmChannel; 89 | this.invert = invert; 90 | this.stripType = stripType; 91 | this.clearOnExit = clearOnExit; 92 | 93 | LOG.info( "LEDs count: {}, GPIO pin: {}, freq hZ: {}, DMA: {}, brightness: {}, pwm channel: {}, invert: {}, " 94 | + "strip type: {}, clear on exit: {}", 95 | ledsCount, gpioPin, frequencyHz, dma, brightness, pwmChannel, invert, stripType, clearOnExit 96 | ); 97 | 98 | init(); 99 | } 100 | 101 | /** 102 | * Set the color of an individual pixel 103 | * 104 | * @param pixel The index of the pixel in the strip 105 | * @param red The red value (0 - 255) 106 | * @param green The green value (0 - 255) 107 | * @param blue The blue value (0 - 255) 108 | */ 109 | public synchronized void setPixel( int pixel, int red, int green, int blue ) { 110 | if ( leds != null ) { 111 | rpi_ws281x.ws2811_led_set( currentChannel, pixel, buildColour( red, green, blue ) ); 112 | } 113 | } 114 | 115 | /** 116 | * Set the color of an individual pixel 117 | * 118 | * @param pixel The index of the pixel in the strip 119 | * @param color The color to set on the pixel 120 | */ 121 | public synchronized void setPixel( int pixel, Color color ) { 122 | setPixel( pixel, color.getRed(), color.getGreen(), color.getBlue() ); 123 | } 124 | 125 | /** 126 | * Set all LEDs in the strip to one color 127 | * 128 | * @param red The red value (0 - 255) 129 | * @param green The green value (0 - 255) 130 | * @param blue The blue value (0 - 255) 131 | */ 132 | public synchronized void setStrip( int red, int green, int blue ) { 133 | for ( int i = 0; i < ledsCount; i++ ) { 134 | setPixel( i, red, green, blue ); 135 | } 136 | } 137 | 138 | /** 139 | * Set all LEDs in the strip to one color 140 | * 141 | * @param color The color to set on the strip 142 | */ 143 | public synchronized void setStrip( Color color ) { 144 | setStrip(color.getRed(), color.getGreen(), color.getBlue()); 145 | } 146 | 147 | /** 148 | * Get the color of a pixel 149 | * 150 | * @return The color of the pixel as a long 151 | */ 152 | public synchronized long getPixel( int pixel ) { 153 | if ( leds != null ) { 154 | return rpi_ws281x.ws2811_led_get(currentChannel, pixel); 155 | } 156 | return 0; 157 | } 158 | 159 | 160 | /** 161 | * Render the current values to the physical light strip. 162 | * 163 | * This method needs to be called after any previous setPixel calls to make the lights change to those colors. 164 | */ 165 | public synchronized void render() { 166 | if ( leds != null ) { 167 | rpi_ws281x.ws2811_render( leds ); 168 | } 169 | } 170 | 171 | private static void initializeNative() { 172 | synchronized ( loaded ) { 173 | if ( !loaded.get() ) { 174 | try { 175 | Path path = Files.createTempFile( "lib" + LIB_NAME, ".so" ); 176 | path.toFile().deleteOnExit(); 177 | Files.copy( Ws281xLedStrip.class.getResourceAsStream( "/lib" + LIB_NAME + ".so" ), path, 178 | StandardCopyOption.REPLACE_EXISTING ); 179 | System.load( path.toString() ); 180 | loaded.set( true ); 181 | LOG.info("Native library loaded"); 182 | } catch ( IOException e ) { 183 | LOG.error( "Error loading library from classpath: ", e ); 184 | 185 | // Try load the usual way... 186 | System.loadLibrary( LIB_NAME ); 187 | loaded.set( true ); 188 | LOG.info("Native library loaded"); 189 | } 190 | } 191 | } 192 | } 193 | 194 | private void init() { 195 | 196 | initializeNative(); 197 | Runtime.getRuntime().addShutdownHook( new Thread( Ws281xLedStrip.this::destroy, "WS281x Shutdown Handler" ) ); 198 | 199 | // Create ref object for pwm channel (?) 200 | leds = new ws2811_t(); 201 | 202 | // Fetch the channel we'll use 203 | LOG.debug( "Fetching current channel..." ); 204 | currentChannel = rpi_ws281x.ws2811_channel_get( leds, pwmChannel ); 205 | 206 | // Initialize all PWM channels 207 | LOG.debug( "Initializing all channels..." ); 208 | 209 | ws2811_channel_t channel; 210 | for ( int i = 0; i < 2; i++ ) { 211 | channel = rpi_ws281x.ws2811_channel_get( leds, i ); 212 | 213 | // Set all to zero (default) 214 | initChannel( channel, 0, 0, 0, false, convertLedTypeToNativeType(stripType) ); 215 | } 216 | 217 | // Initialize current channel 218 | LOG.debug( "Initializing current channel..." ); 219 | 220 | initChannel( currentChannel, ledsCount, gpioPin, brightness, invert, convertLedTypeToNativeType(stripType) ); 221 | 222 | // Initialize LED driver/controller 223 | LOG.debug( "Initializing driver..." ); 224 | 225 | leds.setFreq( frequencyHz ); 226 | leds.setDmanum( dma ); 227 | 228 | // Attempt to initialize driver 229 | LOG.debug( "Attempting to initialize driver..." ); 230 | 231 | ws2811_return_t result = rpi_ws281x.ws2811_init( leds ); 232 | 233 | if ( result.swigValue() != 0 ) { 234 | throw new RuntimeException( "Failed to setup lights driver, result code: " + result ); 235 | } 236 | 237 | LOG.debug( "Initialization complete" ); 238 | } 239 | 240 | private synchronized void destroy() { 241 | try { 242 | if ( leds != null ) { 243 | if (clearOnExit) { 244 | setStrip(0, 0, 0); 245 | render(); 246 | } 247 | rpi_ws281x.ws2811_fini( leds ); 248 | leds.delete(); 249 | leds = null; 250 | currentChannel = null; 251 | } 252 | } catch ( Exception e ) { 253 | LOG.error( "Failed to dispose Ws281x LED controller", e ); 254 | } 255 | } 256 | 257 | private void initChannel( ws2811_channel_t channel, int ledsCount, int gpioPin, int brightness, boolean invert, int stripType ) { 258 | if ( channel != null ) { 259 | channel.setCount( ledsCount ); 260 | channel.setGpionum( gpioPin ); 261 | channel.setInvert( invert ? 1 : 0 ); 262 | channel.setBrightness( (short) brightness ); 263 | channel.setStrip_type( stripType ); 264 | } 265 | } 266 | 267 | public void setBrightness( int brightness ) { 268 | this.brightness = brightness; 269 | currentChannel.setBrightness( (short) brightness ); 270 | } 271 | 272 | private int convertLedTypeToNativeType(LedStripType stripType) { 273 | switch (stripType) { 274 | case SK6812_STRIP_RGBW: 275 | return rpi_ws281xConstants.SK6812_STRIP_RGBW; 276 | case SK6812_STRIP_RBGW: 277 | return rpi_ws281xConstants.SK6812_STRIP_RBGW; 278 | case SK6812_STRIP_GRBW: 279 | return rpi_ws281xConstants.SK6812_STRIP_GRBW; 280 | case SK6812_STRIP_GBRW: 281 | return rpi_ws281xConstants.SK6812_STRIP_GBRW; 282 | case SK6812_STRIP_BRGW: 283 | return rpi_ws281xConstants.SK6812_STRIP_BRGW; 284 | case SK6812_STRIP_BGRW: 285 | return rpi_ws281xConstants.SK6812_STRIP_BGRW; 286 | case WS2811_STRIP_RGB: 287 | return rpi_ws281xConstants.WS2811_STRIP_RGB; 288 | case WS2811_STRIP_RBG: 289 | return rpi_ws281xConstants.WS2811_STRIP_RBG; 290 | case WS2811_STRIP_GRB: 291 | return rpi_ws281xConstants.WS2811_STRIP_GRB; 292 | case WS2811_STRIP_GBR: 293 | return rpi_ws281xConstants.WS2811_STRIP_GBR; 294 | case WS2811_STRIP_BRG: 295 | return rpi_ws281xConstants.WS2811_STRIP_BRG; 296 | case WS2811_STRIP_BGR: 297 | return rpi_ws281xConstants.WS2811_STRIP_BGR; 298 | default: 299 | throw new IllegalStateException("Unknown LED strip type: " + stripType); 300 | } 301 | } 302 | 303 | public int getLedsCount() { 304 | return ledsCount; 305 | } 306 | 307 | public int getGpioPin() { 308 | return gpioPin; 309 | } 310 | 311 | public int getFrequencyHz() { 312 | return frequencyHz; 313 | } 314 | 315 | public int getDma() { 316 | return dma; 317 | } 318 | 319 | public int getBrightness() { 320 | return brightness; 321 | } 322 | 323 | public int getPwmChannel() { 324 | return pwmChannel; 325 | } 326 | 327 | public boolean isInvert() { 328 | return invert; 329 | } 330 | 331 | public LedStripType getStripType() { 332 | return stripType; 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/scripts/createNativeLib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # --------------------------------------------------------------------------------------------------------------------- 4 | # Compiles shared libraries required for interfacing with Raspberry Pi GPIO hardware from Java, using JNI. 5 | # --------------------------------------------------------------------------------------------------------------------- 6 | # Author(s): limpygnome , mbelling 7 | # --------------------------------------------------------------------------------------------------------------------- 8 | 9 | # ********************************************************************************************************************* 10 | # Configuration 11 | # ********************************************************************************************************************* 12 | 13 | #This is needed since somehow the swig command isn't being found on my RasPi 0 otherwise 14 | PATH=$PATH:/usr/local/bin/ 15 | # This defines the JDK to use for JNI header files; automatically picks first dir using ls 16 | JDK_PATH="/usr/lib/jvm" 17 | JDK_DIR=$(ls "${JDK_PATH}" | head -n 1) 18 | JDK_FULL_PATH="${JDK_PATH}/${JDK_DIR}" 19 | 20 | # The libs to include when building C files using GCC 21 | GCC_INCLUDES="-I${JDK_FULL_PATH}/include -I${JDK_FULL_PATH}/include/linux" 22 | 23 | # Relative dir names for input/output 24 | BASE_DIR="$(dirname "$0")/../.." 25 | OUTPUT="${BASE_DIR}/build" 26 | SWIG_SRC="${BASE_DIR}/src/swig" 27 | SWIG_OUT="${OUTPUT}/swig" 28 | SWIG_OUT_JAVA="${OUTPUT}/generatedSource/java/com/github/mbelling/ws281x/jni" 29 | SWIG_PACKAGE_NAME="com.github.mbelling.ws281x.jni" 30 | NATIVE_SRC="${OUTPUT}/ws281x" 31 | NATIVE_OUT="${NATIVE_SRC}/output" 32 | NATIVE_LIB_NAME="ws281x.so" 33 | LIB_BASE_NAME="libws281x" 34 | WRAPPER_LIB_NAME="${LIB_BASE_NAME}.so" 35 | 36 | 37 | # ********************************************************************************************************************* 38 | # Functions 39 | # ********************************************************************************************************************* 40 | 41 | function compileSrc 42 | { 43 | SRC="${1}" 44 | OUT="${2}" 45 | 46 | gcc -shared -fPIC -w -o "${OUT}" -c "${SRC}" -I./ -I${JDK_FULL_PATH}/include 47 | } 48 | 49 | function programInstalled 50 | ( 51 | CMD="${1}" 52 | EXPECTED="${2}" 53 | ERROR="${3}" 54 | SUCCESS="${4}" 55 | 56 | OUTPUT=$(eval ${CMD} || echo "fail") 57 | if [[ "${OUTPUT}" != *"${EXPECTED}"* ]]; then 58 | echo "${ERROR}" 59 | exit 1 60 | else 61 | echo "${SUCCESS}" 62 | fi 63 | ) 64 | 65 | # ********************************************************************************************************************* 66 | # Main 67 | # ********************************************************************************************************************* 68 | echo "**********************[createNativeLib.sh]******************************" 69 | echo "* *" 70 | echo "* NeoPixel ws281x Library Compiler *" 71 | echo "* *" 72 | echo "************************************************************************" 73 | 74 | # Check dependencies installed 75 | set -e 76 | programInstalled "swig -version" "SWIG Version" "Error - SWIG is not installed, cannot continue! Check out the tutorial in this repo's README!" "✅ - SWIG installed..." 77 | programInstalled "java --version" "Runtime Environment" "Error - Java is not installed, cannot continue! (SWIG won't work either w/out java btw... Check out the tutorial in the README!)" 78 | programInstalled "gcc --version" "free software" "Error - GCC is not installed, cannot continue!" "✅ - GCC installed..." 79 | programInstalled "ar --version" "free software" "Error - AR is not installed, cannot continue!" "✅ - AR installed..." 80 | programInstalled "ranlib -v" "free software" "Error - ranlib is not installed, cannot continue!" "✅ - ranlib installed..." 81 | programInstalled "git --version" "git version" "Error - git is not installed, cannot continue!" "✅ - git installed..." 82 | set +e 83 | 84 | # Clean workspace 85 | echo "Deleting build directory to start clean..." 86 | rm -rf build 87 | 88 | # Retrieve rpi_ws281x repository 89 | echo "Cloning rpi_ws281x repository..." 90 | git clone https://github.com/jgarff/rpi_ws281x.git ${NATIVE_SRC} 91 | 92 | # checking out v1.0.0 so we build a consistent library 93 | echo "Checking out specific revision..." 94 | pushd ${NATIVE_SRC} 95 | git checkout v1.0.0 96 | popd 97 | 98 | # Create all the required dirs 99 | echo "Creating required dirs..." 100 | mkdir -p "${SWIG_OUT}" 101 | mkdir -p "${SWIG_OUT_JAVA}" 102 | mkdir -p "${NATIVE_OUT}" 103 | mkdir -p "${OUTPUT}/nativeLib" 104 | 105 | 106 | # Building swig wrapper 107 | echo "Building JNI interface using SWIG..." 108 | 109 | swig -java -outdir "${SWIG_OUT_JAVA}" -package "${SWIG_PACKAGE_NAME}" -o "${SWIG_OUT}/rpi_ws281x_wrap.c" "${SWIG_SRC}/rpi_ws281x.i" 110 | 111 | 112 | # Compile library objects 113 | echo "Compiling ws281x library objects..." 114 | 115 | compileSrc "${NATIVE_SRC}/ws2811.c" "${NATIVE_OUT}/ws2811.o" 116 | compileSrc "${NATIVE_SRC}/pwm.c" "${NATIVE_OUT}/pwm.o" 117 | compileSrc "${NATIVE_SRC}/pcm.c" "${NATIVE_OUT}/pcm.o" 118 | compileSrc "${NATIVE_SRC}/dma.c" "${NATIVE_OUT}/dma.o" 119 | compileSrc "${NATIVE_SRC}/rpihw.c" "${NATIVE_OUT}/rpihw.o" 120 | compileSrc "${NATIVE_SRC}/mailbox.c" "${NATIVE_OUT}/mailbox.o" 121 | 122 | 123 | # Compile library 124 | echo "Compiling ws281x library..." 125 | gcc -shared -o "${OUTPUT}/${NATIVE_LIB_NAME}" "${NATIVE_OUT}/ws2811.o" "${NATIVE_OUT}/pwm.o" "${NATIVE_OUT}/pcm.o" "${NATIVE_OUT}/dma.o" "${NATIVE_OUT}/rpihw.o" "${NATIVE_OUT}/mailbox.o" 126 | 127 | echo "Creating archive..." 128 | ar rc "${NATIVE_OUT}/${LIB_BASE_NAME}.a" "${NATIVE_OUT}/ws2811.o" "${NATIVE_OUT}/pwm.o" "${NATIVE_OUT}/pcm.o" "${NATIVE_OUT}/dma.o" "${NATIVE_OUT}/rpihw.o" "${NATIVE_OUT}/mailbox.o" 129 | 130 | echo "Indexing archive..." 131 | ranlib "${NATIVE_OUT}/${LIB_BASE_NAME}.a" 132 | 133 | 134 | # Compile wrapper into object 135 | echo "Compiling wrapper into object..." 136 | gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC ${GCC_INCLUDES} -Ilib/ -c "${SWIG_OUT}/rpi_ws281x_wrap.c" -o "${SWIG_OUT}/rpi_ws281x_wrap.o" 137 | 138 | 139 | # Compile wrapper into shared lib 140 | echo "Compiling wrapper into shared library..." 141 | gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-z,relro "${SWIG_OUT}/rpi_ws281x_wrap.o" -L${NATIVE_OUT}/ -lws281x -o "${OUTPUT}/nativeLib/${WRAPPER_LIB_NAME}" 142 | 143 | echo "Cleaning up intermediate items..." 144 | rm -rf ${SWIG_OUT} ${NATIVE_SRC} ${NATIVE_OUT} 145 | 146 | echo "Done!" 147 | -------------------------------------------------------------------------------- /src/swig/rpi_ws281x.i: -------------------------------------------------------------------------------- 1 | // SWIG interface file to define rpi_ws281x library python wrapper. 2 | // Author: Tony DiCola (tony@tonydicola.com), Jeremy Garff (jer@jers.net) 3 | 4 | // Define module name rpi_ws281x. This will actually be imported under 5 | // the name _rpi_ws281x following the SWIG & Python conventions. 6 | %module rpi_ws281x 7 | 8 | // Include standard SWIG types & array support for support of uint32_t 9 | // parameters and arrays. 10 | %include "stdint.i" 11 | %include "carrays.i" 12 | 13 | // Declare functions which will be exported as anything in the ws2811.h header. 14 | %{ 15 | #include "../../build/ws281x/ws2811.h" 16 | %} 17 | 18 | // Process ws2811.h header and export all included functions. 19 | %include "../../build/ws281x/ws2811.h" 20 | 21 | %inline %{ 22 | uint32_t ws2811_led_get(ws2811_channel_t *channel, int lednum) 23 | { 24 | if (lednum >= channel->count) 25 | { 26 | return -1; 27 | } 28 | 29 | return channel->leds[lednum]; 30 | } 31 | 32 | int ws2811_led_set(ws2811_channel_t *channel, int lednum, uint32_t color) 33 | { 34 | if (lednum >= channel->count) 35 | { 36 | return -1; 37 | } 38 | 39 | channel->leds[lednum] = color; 40 | 41 | return 0; 42 | } 43 | 44 | ws2811_channel_t *ws2811_channel_get(ws2811_t *ws, int channelnum) 45 | { 46 | return &ws->channel[channelnum]; 47 | } 48 | %} -------------------------------------------------------------------------------- /src/test/java/com/github/mbelling/ws281x/ColorTest.java: -------------------------------------------------------------------------------- 1 | package com.github.mbelling.ws281x; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | public class ColorTest { 8 | @Test 9 | public void testBitShifting() { 10 | Color color = new Color( 64, 128, 192 ); 11 | 12 | long longValue = color.getColorBits(); 13 | 14 | Color newColor = new Color( longValue ); 15 | 16 | assertEquals( 64, newColor.getRed() ); 17 | assertEquals( 128, newColor.getGreen() ); 18 | assertEquals( 192, newColor.getBlue() ); 19 | } 20 | } --------------------------------------------------------------------------------