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

Unable to use Led.Digits with Multple Boards #1834

Open
chrisl8 opened this issue Oct 10, 2023 · 4 comments
Open

Unable to use Led.Digits with Multple Boards #1834

chrisl8 opened this issue Oct 10, 2023 · 4 comments

Comments

@chrisl8
Copy link

chrisl8 commented Oct 10, 2023

I am running Johnny-Five on a Raspberry Pi connected to two Arduino Mega 3's using the Firmata.

LEDs, Buttons, Potentiometers and switches all work fine following the instructions here:
https://johnny-five.io/examples/board-multi/

However, HT16K33 Led.Digits do not. I can set up multiple boards, but only the first Led.Digits instance that I add will work, the others remain blank/off.

I am following the instructions here:
https://johnny-five.io/examples/led-digits-clock-HT16K33/
and then adding the board option, and I can use EITHER board, but if I try to add both, only the first one added works.

Here is the code I am using to test:

const { Boards, Led } = require("johnny-five");

const displays = [
  {
    id: "one",
    port: "/dev/ttyACM3",
    repl: false,
  },
  {
    id: "two",
    port: "/dev/ttyACM0",
    repl: false,
  },
];

const boards = new Boards(displays);

boards.on("ready", () => {
  // This is only here to demonstrate that both boards work
  const boardOneLED = new Led({ board: boards.byId("one"), pin: 13 });
  boardOneLED.blink(500);

  // This is only here to demonstrate that both boards work
  const boardTwoLED = new Led({ board: boards.byId("two"), pin: 13 });
  boardTwoLED.blink(500);

  // Only the first one will work, the second one added will not.
  // Reverse their order to demonstrate.
  const boardOneDigits = new Led.Digits({
    controller: "HT16K33",
    board: boards.byId("one"),
  });
  const boardTwoDigits = new Led.Digits({
    controller: "HT16K33",
    board: boards.byId("two"),
  });

  boardOneDigits.on();
  boardOneDigits.print("1111");
  boardTwoDigits.on();
  boardTwoDigits.print("2222");

  process.on("SIGINT", () => {
    console.log("Turning off displays.");
    boardOneDigits.off();
    boardTwoDigits.off();
  });
});
@dtex
Copy link
Collaborator

dtex commented Oct 11, 2023

This is interesting. Here's what I think is happening...

The HT16K33 is an addressable I2C device. Both of yours start at 0x70, which I believe is the default value. If no address or array of addresses passed, a built-in list of available addresses is used. That list is not built with consideration of multi-board environments, so 0x70 gets shifted off for the first one instantiated and J5 thinks the second one is at the next available address.

I think if you explicitly pass the address or addresses, it will not use the built-in list.

const boardOneDigits = new Led.Digits({
    controller: "HT16K33",
    board: boards.byId("one"),
    address: 0x70
  });
  const boardTwoDigits = new Led.Digits({
    controller: "HT16K33",
    board: boards.byId("two"),
    address: 0x70
  });

@chrisl8
Copy link
Author

chrisl8 commented Oct 11, 2023

Thanks, that makes sense, but when I try it I get this error:

Error: Invalid HT16K33 controller address: 112

It looks like the code has a list of valid addresses, and removes those addresses as they are used here:
https://github.com/rwaldron/johnny-five/blob/094bf6cceb8b9ec424306da8e98308ebd6fa2252/lib/led/ledcontrol.js#L461C37-L461C37

So trying to use the same address twice fails.

I tried passing in a new list of addresses with each call

  const boardOneDigits = new five.Led.Digits({
    controller: "HT16K33",
    board: boards.byId("one"),
    address: 0x70,
    addresses: [0x70],
  });
  const boardTwoDigits = new five.Led.Digits({
    controller: "HT16K33",
    board: boards.byId("two"),
    address: 0x70,
    addresses: [0x70],
  });

but the result is the same.

Oh wait, is this a bug or a feature?
Look at the code here:
https://github.com/rwaldron/johnny-five/blob/094bf6cceb8b9ec424306da8e98308ebd6fa2252/lib/led/ledcontrol.js#L429C1-L462C12

let addresses = new Set([0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77]);

const Controllers = {
  HT16K33: {
    OP: {
      value: {
        SHUTDOWN: 0x20,
        BRIGHTNESS: 0xE0,
        BLINK: 0x80
      }
    },
    initialize: {
      writable: true,
      value(options) {
        const state = priv.get(this);
        const available = Array.from(addresses);

        if (available.length === 0) {
          throw new Error("There are no available HT16K33 controller addresses");
        }

        this.addresses = options.addresses || (options.address ? [options.address] : null);

        // use default range of addresses if addresses aren't specified
        if (this.addresses === null) {
          this.addresses = available.slice(0, state.devices);
        }

        this.addresses.forEach(address => {
          if (!addresses.has(address)) {
            throw new Error(`Invalid HT16K33 controller address: ${address}`);
          }
          addresses.delete(address);
        });

this.addresses is set with options.addresses so I can inject my own list of addresses, however:

It iterates over this.addresses, which I can inject, but then it checks addresses which is hard coded, and it deletes the entry from addresses, so the injected options.addresses seems useless.

Not sure if that is a bug or a feature, but it seems like I can never use 0x70 (112) twice in the code ever.

I'm not sure what the intended behavior is here. Should line 458 check this.addresses?

Oh and now I see addresses is a Set, so it really is intended as a hard coded set of addresses to always be referenced.

Do you see any other way round this without editing ledcontrol.js?

I'm going to experiment with editing ledcontrol.js to get around this. If I find a solution that works for me I can make a PR, but I realize my use case and my code might actually break things for others?

@dtex
Copy link
Collaborator

dtex commented Oct 13, 2023

You could change the address on one of the HT16K33's to 0x71. I imagine it just takes a little bit of solder.

chrisl8 added a commit to chrisl8/johnny-five that referenced this issue Oct 15, 2023
@chrisl8
Copy link
Author

chrisl8 commented Oct 15, 2023

You could change the address on one of the HT16K33's to 0x71. I imagine it just takes a little bit of solder.

Haha, so I could, but for a number of reasons I need this to work without further modifying the hardware on the various installations.

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