Using Jython to drive a Neopixel Strip

Moderators: adafruit_support_bill, adafruit

Forum rules
Talk about Adafruit Raspberry Pi® accessories! Please do not ask for Linux support, this is for Adafruit products only! For Raspberry Pi help please visit: http://www.raspberrypi.org/phpBB3/
Locked
User avatar
dghighfill
 
Posts: 18
Joined: Tue Mar 07, 2017 11:41 pm

Using Jython to drive a Neopixel Strip

Post by dghighfill »

I've been trying to hack together a solution to drive a Neopixel strip from Java. What I'm trying to do is where a web service call comes in, wipe the strip. I'm trying to use the library from the Pi NeoPixel tutorial.

https://github.com/jgarff/rpi_ws281x

Being totally ignorant on Python, it appears that I can use Jython to drive this. Fighting through numerous pathing issues, I was able to call my own Python class from Java but for some reason I am unable to invoke the neopixel.py in the rpi_ws281x/python directory. I've got it so that it will locate the .py file, but the import atexit on line 3 fails. I believe its any import for that matter, because commenting out that one fails the next import _rpi_ws281x as ws.

I've got a really simple SpringBoot application that I've built that loads a mockNeoPixel driver that loads my own neopixle.py. This all works on my Windows pc and this class does nothing but print out some information and was my proof that I could call Python from Java. I'm also using Maven and a jython-standalone dependency, but had many issues with that and it was suggested to copy the Lib directory into my project. I've done that and put them at my src/main/resources/Lib. That seemed to resolve some issues and in fact that is where the atexit.py class is. What's odd is that in my mock neopixel.py class I have the same import atexit, although I'm not using it for anything, and that class seems to work fine. Is there a difference in where I launch that executable jar from? For example my neopixel.py is in a /home/pi/apps/python/neopixel.py directory whereas the API to drive the neopixels is in the /home/pi/rpi_ws281x/python/neopixel.py directory. The jar that I launch with java -jar hackathon.jar is in the /home/pi/apps directory.

I've also found where I need to set the path and I've done that as follows, but I still must be missing something. The code blows up on the pyton.execFile in the @PostContsruct method.

I was able to output my path using the following. The atexit is in the lib/LIB or the jar so not sure why it can't be found.

Code: Select all

import sys
print(sys.path)
['/home/pi/rpi_ws281x/python','/home/pi/apps/hackathon.jar!BOOT-INF/lib/LIB','__classpath__','__pyclasspath__/'

Code: Select all

package com.hackathon;

import java.util.Properties;

import javax.annotation.PostConstruct;

import org.python.core.PyInteger;
import org.python.core.PyLong;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.util.PythonInterpreter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class NeopixelDriver {

	private static final Logger logger = LoggerFactory.getLogger(NeopixelDriver.class);

	@Value(value = "${red}") 
	private String RED = "0";
	
	@Value(value = "${green}") 
	private String GREEN = "0";
	
	@Value(value = "${blue}" )
	private String BLUE = "0";
	
	@Value(value = "${sleepTime}" )
	private String SLEEP_TIME;
	
	@Value(value = "${neoscript}")
	private String NEO_SCRIPT;
	
	private static final int LED_COUNT = 30; // # Number of LED pixels.
	
	private static final int LED_PIN = 18; // # GPIO pin connected to the pixels
											// (must support PWM!).
	private static final long LED_FREQ_HZ = 800000; // # LED signal frequency in
													// hertz (usually 800khz)
	private static final int LED_DMA = 5; // # DMA channel to use for generating
											// signal (try 5)
	private static final int LED_BRIGHTNESS = 200; // # Set to 0 for darkest and
													// 255 for brightest
	private static final String LED_INVERT = "False"; // # True to invert the
														// signal (when using
														// NPN transistor level
														// shift)

	private PythonInterpreter python; 
	private PyObject neoPixel;
	PyObject colorClass;
	private PyObject color;

	@PostConstruct
	public void start() {
		logger.info("Starting initialization of Neopixel Driver");
		// # Create NeoPixel object with appropriate configuration.
		// strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA,
		// LED_INVERT, LED_BRIGHTNESS)
		// # Intialize the library (must be called once before other functions).
		// strip.begin()
		// Call ws2811_init()
		Properties pyProperties = new Properties();
		pyProperties.put("python.path", "./Lib:/home/pi/rpi_ws281x/python" );
		pyProperties.put("python.console.encoding", "UTF-8"); // Used to prevent: console: Failed to install '': java.nio.charset.UnsupportedCharsetException: cp0.
		pyProperties.put("python.security.respectJavaAccessibility", "false"); //don't respect java accessibility, so that we can access protected members on subclasses
		pyProperties.put("python.import.site","false");
		PythonInterpreter.initialize(System.getProperties(), pyProperties, new String[0]);
		python = new PythonInterpreter();
		
		python.execfile(NEO_SCRIPT);
		PyObject neoPixelClass = python.get("Adafruit_NeoPixel");

		PyObject[] args = { new PyInteger(LED_COUNT), 
				new PyInteger(LED_PIN), 
				new PyLong(LED_FREQ_HZ),
				new PyInteger(LED_DMA), 
				new PyString(LED_INVERT), 
				new PyInteger(LED_BRIGHTNESS) };
		neoPixel = neoPixelClass.__call__(args);
		logger.info("Starting up the neopixel");
		
		// Initialize the library (must be called once before other functions).
		neoPixel.invoke("begin");
		
		colorClass = python.get("Color");
		color = colorClass.__call__( new PyInteger(Integer.parseInt(RED)),
				new PyInteger(Integer.parseInt(GREEN)),
				new PyInteger(Integer.parseInt(BLUE)));
		logger.info("Ending initialization of Neopixel Driver");
	}

	public void wipe() throws Exception {
		// LEDs are changed by modifying the color in the .led[index] array and
		// calling ws2811_render().
		// Call ws2811_render()
		logger.info("Wipe the neopixel");
		for (int i = 0; i < LED_COUNT; i++) {
			neoPixel.invoke("setPixelColor", new PyObject[] { new PyInteger(i), color } );
			neoPixel.invoke("show");
			Thread.sleep(Long.parseLong(SLEEP_TIME));
		}
	}
	public void wipe( int red, int green, int blue )throws Exception {
		logger.info("Wipe the neopixel");
		//PyObject colorMethod = python.get("Color");
		color = colorClass.__call__( new PyInteger(red),
				new PyInteger(green),
				new PyInteger(blue));
		
		for (int i = 0; i < LED_COUNT; i++) {
			neoPixel.invoke("setPixelColor", new PyObject[] { new PyInteger(i), color } );
			neoPixel.invoke("show");
			Thread.sleep(Long.parseLong(SLEEP_TIME));
		}
	}
}

And here is the application.properties

Code: Select all

red=255
green=0
blue=0
sleepTime=100
neoscript=/home/pi/rpi_ws281x/python/neopixel.py
#neoscript=src/main/resources/neopixel.py


#Has to be outside the project and can't be in the jar.
mockneoscript=src/main/resources/python/neopixel.py

# On the Pi it needs to be the following or just pass
#--mockneoscript=python/neopixel.py
#mockneoscript=python/neopixel.py
This is my RestController

Code: Select all

package com.hackathon;

import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@RestController(value="/neo")
public class HelloNeoPixel {

	private static final String MESSAGE = "Hello Neo Pixel";
	@Autowired NeopixelDriver driver;

	@RequestMapping
    public String index() throws Exception {
    	driver.wipe();
        return MESSAGE;
    }

    @RequestMapping(params={"r"} )
    public String indexRed(@RequestParam("r") int red) throws Exception {
    	driver.wipe( red, 0, 0 );
        return MESSAGE;
    }
    
    @RequestMapping(params={"g"})
    public String indexGreen(@RequestParam("g") int green) throws Exception {
    	driver.wipe( 0, green, 0 );
        return MESSAGE;
    }
    @RequestMapping(params={"b"})
    public String indexBlue(@RequestParam("b") int blue) throws Exception {
    	driver.wipe( 0,0, blue );
        return MESSAGE;
    }
    @RequestMapping(params={"r","g","b"})
    public String indexColor(@RequestParam("r") int red, @RequestParam("g") int green, @RequestParam("b") int blue) throws Exception {
    	driver.wipe( red, green, blue );
        return MESSAGE;
    }

}
Lastly, the mock neopixel.py that tests out just fine, but obviously doesn't invoke the neopixel, just proves the Java -> Jython -> Python works.

Code: Select all

# Adafruit NeoPixel library port to the rpi_ws281x library.
# Author: Tony DiCola ([email protected]), Jeremy Garff ([email protected])

import atexit

def Color(red, green, blue, white = 0):
	"""Convert the provided red, green, blue color to a 24-bit color value.
	Each color component should be a value 0-255 where 0 is the lowest intensity
	and 255 is the highest intensity.
	"""
	return (white << 24) | (red << 16)| (green << 8) | blue
	

class Adafruit_NeoPixel(object):
	def __init__(self, num, pin, freq_hz=800000, dma=5, invert=False,
			brightness=255, channel=0, strip_type='WS2811_STRIP_RGB'):
		"""Class to represent a NeoPixel/WS281x LED display.  Num should be the
		number of pixels in the display, and pin should be the GPIO pin connected
		to the display signal line (must be a PWM pin like 18!).  Optional
		parameters are freq, the frequency of the display signal in hertz (default
		800khz), dma, the DMA channel to use (default 5), invert, a boolean
		specifying if the signal line should be inverted (default False), and
		channel, the PWM channel to use (defaults to 0).
		"""
		
		print("Number of Pixels: " + str(num))
		print("Pin Number: " + str(pin))
		print("Frequency: " + str(freq_hz))
		print("DMA: " + str(dma))
		print("Brightness: " + str(brightness))
		print("Channel: " + str(channel))
		
	def begin(self):
		print("Begin the neopixel")
		
	def show(self):
		print("Show the neopixel")
		
	def setPixelColor(self, n, color):
		print("Setting neopixel " + str(n) + " to " + str(color) )		

	def numPixels(self):
		print("Returning the number of neopixels as 30")
		return 30
Let me know if the code isn't clear enough, I can post the entire project on GitHub if it would help.

User avatar
dghighfill
 
Posts: 18
Joined: Tue Mar 07, 2017 11:41 pm

Re: Using Jython to drive a Neopixel Strip

Post by dghighfill »

I've now figured out that I can add to my pyton.path by altering the following.

Code: Select all

pyProperties.put("python.path", "U:\\Users\\Dale\\Development\\hackathon\\services\\target\\hackathon.jar!\\BOOT-INF\\classes\\Lib" );
Something in the Jython Plugin or just Python I guess gives me the second entry below which sets up the lib\Lib directory

['U:\\Users\\Dale\\Development\\hackathon\\services\\target\\hackathon.jar!\\BOOT-INF\\classes\\Lib', 'U:\\Users\\Dale\\Development\\hackathon\\services\\target\\hackathon.jar!\\BOOT-INF\\lib\\Lib', '__classpath__', '__pyclasspath__/']

From Spring Boot

Code: Select all

2017-03-19 22:04:42.989  INFO 7888 --- [           main] com.hackathon.MockNeopixelDriver         : Starting initialization of Mock Neopixel Driver
['U:\\Users\\Dale\\Development\\hackathon\\services\\target\\hackathon.jar!\\BOOT-INF\\classes\\Lib', 'U:\\Users\\Dale\\Development\\hackathon\\services\\target\\hackathon.jar!\\BOOT-INF\\lib\\Lib', '__classpath__', '__pyclasspath__/']
2017-03-19 22:04:44.547  WARN 7888 --- [           main] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'helloController': Unsatisfied dependency expressed through field 'driver'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mockNeopixelDriver': Invocation of init method failed; nested exception is Traceback (most recent call last):
  File "../src/main/resources/python/neopixel.py", line 5, in <module>
    import atexit
ImportError: No module named atexit

User avatar
dghighfill
 
Posts: 18
Joined: Tue Mar 07, 2017 11:41 pm

Re: Using Jython to drive a Neopixel Strip

Post by dghighfill »

It's certainly a pathing issue. I took the Lib completely out of the SpringBoot jar that was built. I'm finding that I have to refer to the Python lib as well as the NeoPixel API like below.

Code: Select all

pyProperties.put("python.path", "/home/pi/rpi_ws281x/python:/home/pi/apps/python/Lib" );
I've now made it passed the atexit.py, but now to the next import which is the Neopixel.

Code: Select all

import _rpi_ws281x as ws
Import Error: No module named _rpi_ws281x
I did find this definition buried off in a egg-info class, but not sure if I have to include all those directories on the path as well. One would think, once I load the execFile everything would be relative.

User avatar
adafruit_support_mike
 
Posts: 67446
Joined: Thu Feb 11, 2010 2:51 pm

Re: Using Jython to drive a Neopixel Strip

Post by adafruit_support_mike »

The Python import mechanism *really* wants modules to live in the standard directory tree.

You'll probably find life easier if you use the installer scripts included with the code to do a standard install.

User avatar
dghighfill
 
Posts: 18
Joined: Tue Mar 07, 2017 11:41 pm

Re: Using Jython to drive a Neopixel Strip

Post by dghighfill »

Yea, not know much about Python, I decided to punt on what I was trying to do. I tried to create Python Web Service that I could just call from my Spring app, but I'm getting a funky behavior of the neopixles. I've tried two different strips and cross check my wiring several times. but when it lights up its just random colors and doesn't have any particular pattern like Red or Blue when using the strandtest.py.

While it was a good experiment, sadly, its time to move on.

Locked
Forum rules
Talk about Adafruit Raspberry Pi® accessories! Please do not ask for Linux support, this is for Adafruit products only! For Raspberry Pi help please visit: http://www.raspberrypi.org/phpBB3/

Return to “Adafruit Raspberry Pi® accessories”