0

I am trying to send rotary encoder output from an UNO to seven Trinket M0s and a computer via a (powered) USB hub.

enter image description here

Currently the UNO → Laptop bit works great, serial monitoring works via the hub for everyone and I'm able to get a master/slave connection going between the UNO and a Trinket via Arduino's Wire library. But Wire uses pins, and I haven't had any luck finding a similar solution for USB. Thus far, the best leads seem to be around using something like stty in a bash script to listen for what each port is up to but I'm so far in over my head at this point that that might just be wishful thinking.

I'm not looking for a solution here, just some suggestions as to where to head next. Specifically:

  1. Is the Wire library useful for USB communication? Is there something similar (master/slave-wise) that would be helpful?
  2. Assuming the serial/listen thing works, are there good ways to re-route data to different ports? Currently my best guess is something like $ echo $encoderVal > /dev/cu.usbmodem????? to each one individually ... but that might be a completely noobish notion.)
  3. i2c keeps on coming up in searches, but looks like overkill. Is that a direction to head?
  4. Is this all completely nuts?

Anyways, thanks to everyone in advance.

Update

Ok! Thanks to the advice of @Gerben and @chrisl I ended up routing everything around with PySerial, and (for the extremely limited purposes of this little project) it works well! Here's what is currently working:

import serial
from time import sleep
import os

# Connect to each Arduino individually
serialUno = serial.Serial("/dev/cu.usbmodem141101", 9600, timeout=0)
serialTrinket1 = serial.Serial("/dev/cu.usbmodem1414401", 9600, timeout=0)
# (+ 6 more. serialTrinket2, serialTrinket3, etc)

# Open a text file to save the encoder value in
f = open("encoderVal.txt", "w")

while True:
  # Read encoderVal from the UNO
  data = serialUno.read(9999)
  if len(data) > 0:

    # Send data to each trinket
    serialTrinket1.write(data)

    # Save val to text file to Unity
    with open('encoderVal.txt', 'r+') as f:

      # Wipe the file so it's only the most recent value
      f.truncate()
      dataStripped = data.strip()

      # Return the last int in the data stream
      dataSplit = dataStripped.split('\n')
      f.write(dataSplit[-1])

  sleep(0.05)

# Close serial connections & file on interrupt
serialUno.close()
serialTrinket1.close()
f.close()

As for the laptop end of the equation, I abandoned the serial library that I had been using in favor of this Unity-to-Python tool, which works perfectly.

The only issue thus far is that PySerial loses the occasional few bytes here and there, which outputs a wonky number ever 30 or so. That'll be tomorrow's project. Thanks everyone for your help!

  • Why not using Serial (UART) for this? You can connect one transmitter board (the Uno in this case) to multiple receiver boards ( the Trinkets) – chrisl May 25 at 8:38
  • I'd use something like Python on the PC, to connect to all the serial ports of the UNO and Trinkets. Have the Python code parse the incoming data (i.e. rotary encoder position), and send it to those boards that need that data. Or even simpler. Read incoming serial data, and send it to all available serial ports (kind of like a repeater). The only thing I don't understand is why you even have a PC in your setup. What's its use? Using something other that USB, means you could run the whole system without the PC attached. – Gerben May 25 at 10:33
  • @chrisl Ok, but would that not be adding extra complexity? (First search brought up Emulating UART over USB which gets us to virtual COM ports and ... more. – laffan May 25 at 16:07
  • @Gerben That seems like a solid plan. I'll start researching serial & python. Re: your question, the PC has to be there because the serial input from the rotary encoder is running a Unity game (the whole HDMI side of this not in the diagram.) – laffan May 25 at 16:07
0

Is the Wire library useful for USB communication? Is there something similar (master/slave-wise) that would be helpful?

The Wire library does I2C communication, which has absolutely nothing to do with USB. Actually the USB connection on the Uno is not native. The Atmega328P on the Uno only has a Serial (UART) interface, which is onboard connected to another chip (Atmega82U or others), which has native USB support. That chip will do the USB communication. The driver on the PC will then emulate a Serial COM port, so that it is something like a "Serial COM port over USB" connection. USB is complex and you should not tinker with it directly, unless you are very advanced in this direction. And I don't think, doing that would help you here.

Assuming the serial/listen thing works, are there good ways to re-route data to different ports? Currently my best guess is something like $ echo $encoderVal > /dev/cu.usbmodem????? to each one individually ... but that might be a completely noobish notion.)

Since you have a complete Unity game, you have to program something in that direction. Using the bash seems not good here. I don't know, if you can communicate with serial COM ports from Unity (since I haven't worked with it yet), but doing this in Python works like a charm. There are many tutorials out there, that show how to do it.

i2c keeps on coming up in searches, but looks like overkill. Is that a direction to head?

If that is an overkill depends on your exact requirements for the system. Sure, the I2C protocol has a bit of overhead, but maybe you need the functionality.

Is this all completely nuts?

Currently it seems a bit like that. I think, this due to you not having a good structure for the project. To help you with that, I will explain some possibilities below.


As I understood, you have the following setup: The Uno has a rotary encoder, that controls a game on the PC. All boards (Uno and Trinkets) have one LED strip, that should light up depending on what happens in the game.

You have now 2 basic structures for the communication:

  1. Doing everything over an own USB connection. This is what you sketched above. Every board has it's own serial COM port (which actually get's emulated over USB by the driver) on the PC and your game/program on it has to connect to all these and send the appropriate data. Sure this is a possible design, but it comes with downsides: Currently it doesn't seem, that you already have implemented the communication program. That is important in this design, but that's not really about Arduino and depends on how you are programming the game in Unity (haven't done that before and don't know how to connect a serial device to it. Maybe through a python script). The second problem is, that you will have choose the serial port for every board, when you start the game. The port numbers/names are not always the same; it's up to the operating system to set these. That's not a great usablity.

  2. You can use other communication interfaces between the boards directly. In this case only the Uno would be connected via USB to the PC and every data would go through the Uno. What kind of interface you should use depends on your exact requirements. Since you didn't specify them in detail, I will explain some examples:

    • Every Trinket get's the exact same data and nothing must be read from them: In this case you can use the Serial (UART) interface. It uses the pins, that are marked with RX (receiving pin) and TX (transmitting pin). Normally this connection is only between 2 boards, but it is possible to simply connect multiple receivers (with their RX pin) to exactly 1 transmitter (with it's TX pin).

      The Uno has one hardware Serial interface, which is also used to communicate with the PC. If it is OK, that the PC also get's the data, which is meant for the Trinkets, (and the Trinkets get the data, that are meant for the PC) you can use this interface and wire the Uno's TX to all Trinket RX pins. You can google how to read data from the interface with the Serial library, which is directly available with Arduino. If you don't want the PC to get the same data, you can set up a SoftwareSerial interface (with the library with that name). That provides the same interface, but is handled in software, because the Uno doesn't have another hardware Serial (UART) interface.

      In both cases you would transmit the data from the PC (data out of the game) via the USB serial port to the Uno, which then in turn sends it further to all the Trinket boards. They read the data and act accordingly. Everytime the Uno senses new rotary encoder positions, it will send data to the PC.

      For connected the Uno with a Trinket, you only need 2 wires in this design (1 for Uno TX to Trinket RX and 1 ground wire). Of course, you also would have to provide power to the Trinkets. Please see below for that matter.

    • Every Trinket can be address individually and data can also be read from them: In this case I2C can be the way to go. For the communication you need at least 3 wires (SDA, SCL and ground). Every Trinket joins the I2C bus with a configured address. The Uno acts as master on the bus. He can call a Trinket by it's address and send or request data. The I2C protocol is more structured than serial (UART), because it is divided into transmissions. This works great, if the Trinkets can all get different data, and if you also want to read data from them. Be sure to give very Trinket a unique address on the bus. For the program part look into the Wire library.

    • Individual sending and receiving in a very fast manner: If you need the individual data transmitting and reading from I2C, but you also need the communication to be very fast, you can use SPI. The SPI bus doesn't use addresses, but Slave Select pins. You need 1 pin on the master (Uno) for every slave device. If you want to communicate with one Trinket, you pull it's Slave Select pin to LOW. This bus needs 5 wires (MISO, MOSI, SCK, Slave Select and ground), but the communication can be very fast; way faster than I2C. Look into the SPI libary for the programming of this.


Providing power: Depending on your design and the number and type of the LEDs, you have to look into, how you supply the needed power to the boards. In the design, that you sketched above, (every board directly to USB) the boards themselves should have enough power, but LED strips can be very power hungry, getting fast to currents over 1A. The pinout of Adafruits Trinket M0 shows a maximal current of 500mA from the Vusb pin. The 5V pin of the Uno can handle about 200mA (as I remember, currently not sure. You can google this).

If you choose a design with only the Uno connected to USB, you might need an extra power supply, so that you can provide the power for all Trinkets and their LEDs with it. Please adhere to the current limits of the components, or you may fry your boards.

I hope, that help's. Have fun

  • Thanks for putting all the time in to this answer. As you'll see I ended up with a far hackier solution than you'd probably advise, but hey, it kinda works! – laffan May 27 at 8:28
1

First, read a bit about USB, it is an entire protocol, not just a hardware/pin based interface. It is actually quite involved. Then it will be clearer to you why a USB (from the Nano) solution will be... difficult.

Based on my understanding, I would use your #2 idea and what has been suggested, and do the following...

  1. Setup the Uno as a USB slave, not a master.
  2. Using the PC, connect to the Uno and the Trinkets.
  3. When the PC gets data from the Uno have the PC "repeat" it to the Trinkets.

I think this will be the easiest and quickest solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.