firmata / protocol

Documentation of the Firmata protocol.
996 stars 151 forks source link

Add DAC support #32

Open soundanalogous opened 9 years ago

soundanalogous commented 9 years ago

The Arduino Zero has 1 10-bit DAC pin. Draft a protocol to add DAC support to Firmata.

soundanalogous commented 9 years ago

looks like this is simply analogWrite() on pin A0 of the Arduino Zero, but it's 10-bit instead of 8-bit.

jjorsett commented 9 years ago

Teensy 3.1/3.2 has a 12-bit DAC on its A14/DAC pin.

PaulStoffregen commented 9 years ago

Arduino Due has 2 DACs. A future Teensy will too.....

rwaldron commented 9 years ago
rwaldron commented 9 years ago

SYSEX extension:

0   START_SYSEX (0xF0)
1   sysex command (0x00-0x7F) ? Can be assigned any available
2   pin (0-127)
3   lsb
4   msb
N   END_SYSEX (0xF7)

... which actually looks a lot like https://github.com/firmata/protocol/blob/master/protocol.md#extended-analog

soundanalogous commented 9 years ago

The way DAC works on Arduino is you simply use analogWrite. Now that I think about it, a new protocol should not even be necessary for writing the value at least. It should just work if you send the value on the appropriate pin. So for a 10-bit DAC you'd send between 0 and 1024, for a 12-bit you'd send between 0 and 4096, etc. I just need to make sure Firmata is not limiting the value when it's passed to analogWrite.

However, I will have to add a way to set the analogWriteResolution value.

soundanalogous commented 9 years ago

Thinking more about this, there are 3 options for adding DAC support:

  1. Simply use existing PWM pinMode and report the DAC resolution via the capability query response.
  2. Add a new DAC pinMode, but still use the analog write (or extended analog write if conditions are met) message. Report the DAC resolution via the capability query response (but reporting a DAC pin type vs PWM pin type.
  3. Add a new DAC pin and a new SYSEX message for writing to DAC pins. This may make the most sense if non-arduino platforms separate the concept of analogWrite and DAC.

Any preferences? I'm leaning towards 2 or 3. My concern is 1 or 2 may be too Arduino-specific.

Bucknalla commented 5 years ago

@soundanalogous I'm currently implementing a host library based on the Arduino Firmata for an EFM32. I have a slightly different problem that goes beyond just support for a DAC (my preference would be for option 3). The BGM111 MCU that I am looking to support has an IDAC instead of a typical DAC. It'd be great to have a way to set up and configure what analogWrite() is actually doing, possibly through a new SYSEX command which defines what peripheral that analogWrite() is targetting.

soundanalogous commented 5 years ago

Does the BGM111 currently use analogWrite for the DAC?

Bucknalla commented 5 years ago

The BGM111 does not have a Voltage DAC so I've implemented analogWrite() to use PWM at this moment in time. I'd like to support the use of the Current DAC but am not sure the best means to support it with Firmata as of right now. IMO it should be mappable to the analogWrite() function with some means to be able to choose how you drive it, i.e. be able to select if you want to use PWM, Voltage DAC, Current DAC, etc. on a specific Pin.

Revisiting your earlier comment https://github.com/firmata/protocol/issues/32#issuecomment-152777878, I think number 2 would actually make more sense for this kind of implementation.

soundanalogous commented 5 years ago

Ideally Arduino would implement a way consistent way to write to a DAC or IDAC in their HAL and Firmata would simply use that API rather than having to maintain different implementations for different boards.

Bucknalla commented 5 years ago

That would be ideal; even so, you'd still need a way to choose (from a Firmata client) between which peripheral that you wanted to drive the analogPin?

soundanalogous commented 5 years ago

Looking again at how you write to the DAC on an Arduino, it requires 2 functions calls, especially if you're also writing to a PWM pin at a different resolution than the DAC. So if you wanted to write a value to a 12 bit DAC then write an 8-bit PWM value to another pin you'd have to do something like this:

analogWriteResolution(12);
analogWrite(DAC_PIN, val12bit);

analogWriteResolution(8);
analogWrite(PWM_PIN, val8bit);

With Firmata that would require sending 4 separate messages which is slightly inefficient. Considering that, it may be better to add a DAC_WRITE message to Firmata. The DAC resolution for any supported pins would already be known (configured in Boards.h) so the resolution could be set automatically without having to pass a separate message, then set back to the default PWM resolution immediately afterwards. Much simpler IMO.

0  START_SYSEX              (0xF0)
1  DAC_WRITE                (0x5B)
2  pin                      (0-127)
3  bits 0-6                 (least significant byte)
4  bits 7-13                (most significant byte)
... additional bytes may be sent if more bits are needed
N  END_SYSEX                (0xF7)

In Arduino code:

  1. Parse the DAC_WRITE command
  2. Set the analog write resolution
  3. Write to the DAC pin
  4. Reset the analog write resolution
// something like this would be added to StandardFirmata in the
// existing sysexCallback function
void sysexCallback(byte command, byte argc, byte *argv)
{
// ...
  switch (command) {
  // ...
    case DAC_WRITE:
      if (argc > 1) {
        byte pin = argv[0];
        int val = argv[1];
        if (argc > 2) val |= (argv[2] << 7);
        if (argc > 3) val |= (argv[3] << 14);
        analogWriteResolution(DEFAULT_DAC_RESOLUTION); // define in Boards.h
        analogWriteCallback(pin, val);
        analogWriteResolution(DEFAULT_PWM_RESOLUTION); // already defined in Boards.h
      }
      break;
     // ...
  }
}