Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Neopixel lib doesn't behave well with esp32-s3 boards that have an on board Neopixel #1188

Closed
kairos0ne opened this issue Aug 4, 2023 · 8 comments

Comments

@kairos0ne
Copy link

Build environment: macOS
Moddable SDK version: 3.9.4-88-ga7fc383c
Target device: m5atom_s3_lite and s3_tft_feather

Description
I think something is going on with the Neopixel implementation on these boards when using the neopixel lib with an external neopixel strip or ring it doesn't behave in the same way as using the same lib, same code tested across atom_s3 and works fine. In fact any of the esp32-s3 boards work expect the ones with an onboard neopixel. I may be missing something as there isn't much docs on this lib but there's an example blog post.

Steps to Reproduce
This is the main.js

import { BLEServer } from "bleserver";
import { uuid, typedValueToBuffer } from "btutils";
import Timer from "timer";
import NeoPixel from 'neopixel';


// UUIDs for the UART service and its characteristics
const SERVICE_UUID = uuid`6E400001-B5A3-F393-E0A9-E50E24DCCA9E`;
const CHARACTERISTIC_RX_UUID = uuid`6E400002-B5A3-F393-E0A9-E50E24DCCA9E`;
const CHARACTERISTIC_TX_UUID = uuid`6E400003-B5A3-F393-E0A9-E50E24DCCA9E`;

const timeout = 30000; // 30 seconds
const duration = 0; // unlimited

const UART_SERVER = "UART Server";

// pin for the neopixel
const neopixelPin = 1;

let neopixel = new NeoPixel({ length: 12, pin: neopixelPin, order: "GRB" });

const red = neopixel.makeRGB(255, 0, 0);
const yellow = neopixel.makeRGB(255, 255, 0);
const orange = neopixel.makeRGB(255, 165, 0);
const purple = neopixel.makeRGB(128, 0, 128);
const pink = neopixel.makeRGB(255, 192, 203);
const cyan = neopixel.makeRGB(0, 255, 255);
const magenta = neopixel.makeRGB(255, 0, 255);
const green = neopixel.makeRGB(0, 255, 0);
const blue = neopixel.makeRGB(0, 0, 255);
const white = neopixel.makeRGB(255, 255, 255);
const black = neopixel.makeRGB(0, 0, 0);

function setAll(color) {
	for (let i = 0; i < neopixel.length; i++) {
		neopixel.setPixel(i, color);
	}
	neopixel.update();
}

function clearAll() {
	setAll(black);
}

function setAllColor(color) {
	setAll(color);
}

// create an array of the colours to be used
let colors = [black, red, yellow, orange, purple, pink, cyan, magenta, green, blue, white ];

// create a variable to hold the current colour
let currentColor = 0;

button.a.onChanged = function () {
	if (this.read()) {
		trace("Button A: ", this.read(), "\n");
		setAllColor(colors[currentColor]);
		currentColor++;
		if (currentColor >= colors.length) {
			currentColor = 0;
		}
		setAllColor(colors[currentColor]);
	}
}

class UARTServer extends BLEServer {

	onReady() {
		this.deviceName = "VEGAONE";
		this.onDisconnected();
	}
	onConnected() {
		this.stopAdvertising();
	}
	onCharacteristicNotificationEnabled(characteristic) {
		if (characteristic.uuid.equals(CHARACTERISTIC_TX_UUID)) {
			this.tx = characteristic;
			this.timer = Timer.repeat(id => {
				if (this.tx_characteristic) {
					this.tx.notify("Hello from UART Server\n");
				}
			}, 1000);
		} else if (characteristic.uuid.equals(CHARACTERISTIC_RX_UUID)) {
			this.rx = characteristic;
		}

	}

	onCharacteristicNotificationDisabled(characteristic) {
		if (characteristic.uuid.equals(CHARACTERISTIC_TX_UUID)) {
			Timer.clear(this.timer);
			this.timer = null;
			this.disconnect();
		}
	}
	onDisconnected() {
		this.tx = null;
		this.rx = null;
		// remove the timer
		if (this.timer) {
			Timer.clear(this.timer);
			this.timer = null;
		}
		this.startAdvertising({
			advertisingData: { flags: 6, completeName: this.deviceName, completeUUID128List: [SERVICE_UUID] }
		});
	}

	onCharacteristicWritten(characteristic, value) {

		if (characteristic.uuid.equals(CHARACTERISTIC_RX_UUID)) {
			trace.left(value, UART_SERVER);

			if (value == 1) {
				setAllColor(black);
			}

			if (value == 2) {
				setAllColor(red);
			}

			if (value == 3) {
				setAllColor(yellow);
			}

			if (value == 4) {
				setAllColor(orange);
			}

			if (value == 5) {
				setAllColor(purple);
			}

			if (value == 6) {
				setAllColor(pink);
			}

			if (value == 7) {
				setAllColor(cyan);
			}

			if (value == 8) {
				setAllColor(magenta);
			}

			if (value == 9) {
				setAllColor(green);
			}

			if (value == 10) {
				setAllColor(blue);
			}

			if (value == 11) {
				setAllColor(white);
			}

			if (value == 12) {
				setAllColor(black);
			}

			if (value == 13) {
				clearAll();
			}

		}
	}

}
let server = new UARTServer;

This is the maifest

{
	"include": [
		"$(MODDABLE)/examples/manifest_base.json",
		"$(MODDABLE)/modules/pins/digital/manifest.json",	
		"$(MODDABLE)/modules/network/ble/manifest_server.json",
		"$(MODDABLE)/modules/pins/pwm/manifest.json"
	],
	"modules": {
		"*": [
			"$(MODULES)/drivers/neopixel/*",
			"./main"
		]
	},
	"preload": "neopixel",
	"platforms": {
		"esp32": {
			"modules": {
				"*": "$(MODULES)/drivers/neopixel/esp32/*"
			}
		},
		"...": {
			"error": "unsupported platform"
		}
	},
	"ble":{
		"*": [
			"./bleservices/*"
		]
	}
}

On the atom_s3_lite and tft_feather it bugs out on button press and Im not sure the currentColor gets updated, its almost as if every time I use the neopixel the code initialises again and sets it back to 0 . Worth also noting that they cause a panic in BLE that disconnects the device when you try write a packet. Should I be doing something different with the boards that have an onboard neopixel ?

Expected behavior
I would expect the atom _s3 and the atom_s3_lite to behave the same with the same code. However as I say its strange how these boards only with onboard neopixel are behaving strangely as it also works on the m5stack_cores3, so I could be missing some fundamental documentation for how to handle libs that have them baked in neopixel.

Other information
Works on atom_s3, m5stack_cores3

Doesn't work on atom_s3_lite and tft_feather_s3

@phoddie
Copy link
Collaborator

phoddie commented Aug 4, 2023

It sounds like you may have a conflict with the built-in Neopixel on the board. Most hosts set-up that Neopixel to animate rainbow colors. Try turning that off by adding this fragment to your project's manifest.json (if your project's manifest already has a "config" section, just add the "led" section to that).

	"config": {
		"led": {
			"rainbow": false
		},
	},

@kairos0ne
Copy link
Author

I have added config to the manifest and addresses the BLE issue the device no longer panics and disconnects when writing a packet that in turn sets a color. So the TFT feather works great now over BLE thanks for the tip.

However, I don't see any improvement on the atoms3_lite. I wonder as I'm using the button to drive change of color on the neopixel on atoms3_lite, let me check quick if BLE works nicely on it and i will report back. However I think the code I have for the button press is not quite right somehow for the atoms3_lite. Something I noted is the last pixel losses its connection and goes out about a second after you press the button and when you click again its sets red again meaning the code is initialising again somehow as I'm setting currentColor to 0 and in the global scope. Red is the first color at index 1

Are there differences to the button implementations for the atoms3 and atoms3_lite respectively ?

@kairos0ne
Copy link
Author

So I can report atom-s3-lite causes a panic on write for BLE even though the config you suggested is present. And it bugs out on button press also suggesting even though the config is there, there is still a conflict somewhere.

@phoddie
Copy link
Collaborator

phoddie commented Aug 7, 2023

It looks like the esp32/m5atom_s3_lite port does not have the option to disable the built-in NeoPixel. That would explain why it behaves differently from the other devices. Here's the relevant code, not there's no if block around it:

export default function (done) {
globalThis.button = {
a: new M5Button(41)
};
globalThis.lights = new NeoPixel({});
done();

That behavior is consistent across M5Atom targets and M5Stack Fire. To avoid breaking things, it would probably be best to initialize NeoPixels from the global lights on first access rather than at start-up. I don't have a device to try myself right now but please try replacing line 32 above with this:

	Object.defineProperty(globalThis, "lights", {
		enumerable: true,
		configurable: true,
		get() {		// instantiate lights on first access
			const value = new NeoPixel({}); 
			Object.defineProperty(globalThis, "lights", {
				enumerable: true,
				configurable: true,
				writable: true,
				value
			});
			return value;
		}
	});

@kairos0ne
Copy link
Author

That works well now, all burning brightly and changing on button press now. Ble works great too.

@kairos0ne
Copy link
Author

HMOriginal-B5749DE0-9133-458F-AA4D-C5C7606D0A4F

@phoddie
Copy link
Collaborator

phoddie commented Aug 7, 2023

Very nice! Always good to see a glowing Neopixel image here. ;)

I'll apply the patch above to the M5Atom & Fire ports so the behavior is consistent.

@phoddie
Copy link
Collaborator

phoddie commented Aug 20, 2023

Closing - fixes now committed.

@phoddie phoddie closed this as completed Aug 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants