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

Add support for OpenDeck boards #1746

Open
wants to merge 41 commits into
base: master
Choose a base branch
from

Conversation

paradajz
Copy link

@paradajz paradajz commented Sep 30, 2021

This PR adds support for OpenDeck boards.

What is OpenDeck?

OpenDeck is open-source platform for building MIDI controllers which supports many different boards (some AVR, some STM32). More info on the website. Since firmware version 6.0.0, I have added support for DMX - more specifically, through CDC interface, Enttec Widget API was implemented so that the boards are compatible with OLA. A drawback for my boards was that in default USB Pro implementation, DMX data was sent constantly to the boards, even if data hasn't changed. Since my boards do many other things other than DMX, performance issues arose (at least on AVR, STM32 can handle it). I've started discussion about it here.

What this PR does is:

  • Copies UltraDMX Pro device/widget files and renames them to OpenDeck device/widget files, respectively (UltraDMX Pro seemed to be the simplest one on which I could base my boards)
  • Implements only one output port (no input)
  • Changes a bit handling of serial numbers so that in OLA, they show up the same as USB serial number which I already use on my boards
  • Changes the handling of firmware version which gets shown in OLA Web interface (displayed in major.minor format)
  • Sends DMX data on board only if something has changed, and then:
    • If the amount of changed channels is > 128, full DMX frame is sent
    • Otherwise, send only the changed channels (two bytes for channel, one byte for value)

@peternewman peternewman linked an issue Oct 1, 2021 that may be closed by this pull request
Copy link
Member

@peternewman peternewman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial linting comments to start off with

plugins/usbpro/OpenDeckDevice.cpp Outdated Show resolved Hide resolved
plugins/usbpro/OpenDeckDevice.cpp Outdated Show resolved Hide resolved
plugins/usbpro/OpenDeckDevice.cpp Outdated Show resolved Hide resolved
plugins/usbpro/OpenDeckDevice.cpp Outdated Show resolved Hide resolved
plugins/usbpro/OpenDeckDevice.h Outdated Show resolved Hide resolved
plugins/usbpro/OpenDeckDevice.h Outdated Show resolved Hide resolved
plugins/usbpro/OpenDeckWidget.cpp Outdated Show resolved Hide resolved
Copy link
Member

@peternewman peternewman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plugins/usbpro/OpenDeckDevice.cpp Outdated Show resolved Hide resolved
plugins/usbpro/OpenDeckDevice.cpp Outdated Show resolved Hide resolved
plugins/usbpro/OpenDeckWidget.cpp Outdated Show resolved Hide resolved
plugins/usbpro/OpenDeckWidget.h Outdated Show resolved Hide resolved
plugins/usbpro/OpenDeckDevice.h Outdated Show resolved Hide resolved
@paradajz paradajz force-pushed the support_for_opendeck branch 2 times, most recently from 7a2f576 to 8073f38 Compare October 22, 2021 18:55
@peternewman
Copy link
Member

I'm pleased to say the CI is now progressing a lot further than it was. You just need to implement your widget in the tests and I think you'll be good.

The relevant file is:
https://github.com/OpenLightingProject/ola/blob/master/plugins/usbpro/WidgetDetectorThreadTest.cpp

You can also run the tests locally with the usual make check.

You might also like to add a test for your firmware versioning (possibly pulling it out into a standalone function too).

@paradajz
Copy link
Author

paradajz commented Oct 23, 2021

I'm pleased to say the CI is now progressing a lot further than it was. You just need to implement your widget in the tests and I think you'll be good.

The relevant file is: https://github.com/OpenLightingProject/ola/blob/master/plugins/usbpro/WidgetDetectorThreadTest.cpp

You can also run the tests locally with the usual make check.

You might also like to add a test for your firmware versioning (possibly pulling it out into a standalone function too).

I'm pretty lost with these tests... Not sure what the tests are doing, what's the expected data and how to interpret error I'm getting when running them, which is:

==================================
   OLA 0.10.8: ./test-suite.log
==================================

# TOTAL: 82
# PASS:  81
# SKIP:  0
# XFAIL: 0
# FAIL:  1
# XPASS: 0
# ERROR: 0

.. contents:: :depth: 2

FAIL: plugins/usbpro/WidgetDetectorThreadTester
===============================================

.common/thread/Thread.cpp:200: Thread , policy SCHED_OTHER, priority 0
plugins/usbpro/WidgetDetectorThread.cpp:399: trying stage 0 for 0x6e5d80
plugins/usbpro/UsbProWidgetDetector.cpp:537: Detected USB Device: ESTA Id: 0x7a70 (Open Lighting), device Id: 0x0001 (Unittest Device), serial: 0x12345678, f/w version: N/A
.common/thread/Thread.cpp:200: Thread , policy SCHED_OTHER, priority 0
plugins/usbpro/WidgetDetectorThread.cpp:399: trying stage 0 for 0x7f11a0001730
plugins/usbpro/UsbProWidgetDetector.cpp:537: Detected USB Device: ESTA Id: 0x6864 (JESE), device Id: 0x0002 (RDM-TRI), serial: 0x12345678, f/w version: N/A
.common/thread/Thread.cpp:200: Thread , policy SCHED_OTHER, priority 0
plugins/usbpro/WidgetDetectorThread.cpp:399: trying stage 0 for 0x7f11a00015c0
plugins/usbpro/UsbProWidgetDetector.cpp:537: Detected USB Device: ESTA Id: 0x4744 (Goddard Design), device Id: 0x444d (DMXter4), serial: 0x12345678, f/w version: N/A
.common/thread/Thread.cpp:200: Thread , policy SCHED_OTHER, priority 0
plugins/usbpro/WidgetDetectorThread.cpp:399: trying stage 0 for 0x7f11a0001af0
plugins/usbpro/UsbProWidgetDetector.cpp:537: Detected USB Device: ESTA Id: 0x0000, device Id: 0x0000, serial: 0x12345678, f/w version: 1.4
plugins/usbpro/WidgetDetectorThread.cpp:324: Defaulting to a Usb Pro device
plugins/usbpro/WidgetDetectorThread.cpp:340: USB Pro Firmware >= 2.4 is required for RDM support, this widget is running 1.4
.common/thread/Thread.cpp:200: Thread , policy SCHED_OTHER, priority 0
plugins/usbpro/WidgetDetectorThread.cpp:399: trying stage 0 for 0x7f11a0001a50
plugins/usbpro/UsbProWidgetDetector.cpp:537: Detected USB Device: ESTA Id: 0x0000, device Id: 0x0000, serial: 0x12345678, f/w version: 1.4
plugins/usbpro/WidgetDetectorThread.cpp:324: Defaulting to a Usb Pro device
plugins/usbpro/WidgetDetectorThread.cpp:326: Found and unlocked an Enttec USB Pro Mk II
plugins/usbpro/WidgetDetectorThread.cpp:340: USB Pro Firmware >= 2.4 is required for RDM support, this widget is running 1.4
.common/thread/Thread.cpp:200: Thread , policy SCHED_OTHER, priority 0
plugins/usbpro/WidgetDetectorThread.cpp:399: trying stage 0 for 0x7f11a0004140
plugins/usbpro/UsbProWidgetDetector.cpp:537: Detected USB Device: ESTA Id: 0x0000, device Id: 0x0000, serial: 0x12345678, f/w version: 1.4
plugins/usbpro/WidgetDetectorThread.cpp:324: Defaulting to a Usb Pro device
plugins/usbpro/WidgetDetectorThread.cpp:326: Found and unlocked an Enttec USB Pro Mk II
plugins/usbpro/WidgetDetectorThread.cpp:340: USB Pro Firmware >= 2.4 is required for RDM support, this widget is running 1.4
.common/thread/Thread.cpp:200: Thread , policy SCHED_OTHER, priority 0
plugins/usbpro/WidgetDetectorThread.cpp:399: trying stage 0 for 0x7f11a0004500
plugins/usbpro/UsbProWidgetDetector.cpp:323: USB Widget didn't respond to messages, esta id 0, device id 0
plugins/usbpro/UsbProWidgetDetector.cpp:326: Is device in USB Controller mode if it's a Goddard?
plugins/usbpro/WidgetDetectorThread.cpp:399: trying stage 1 for 0x7f11a0004500
plugins/usbpro/RobeWidgetDetector.cpp:203: Detected Robe Device, UID : 5253:0200000a, Hardware version: 0x1, software version: 0xb, eeprom version 0x3
.common/thread/Thread.cpp:200: Thread , policy SCHED_OTHER, priority 0
plugins/usbpro/WidgetDetectorThread.cpp:399: trying stage 0 for 0x7f11a0002170
plugins/usbpro/UsbProWidgetDetector.cpp:537: Detected USB Device: ESTA Id: 0x6a6b (DMXking.com), device Id: 0x0002 (ultraDMX Pro), serial: 0x12345678, f/w version: N/A
.common/thread/Thread.cpp:200: Thread , policy SCHED_OTHER, priority 0
plugins/usbpro/WidgetDetectorThread.cpp:399: trying stage 0 for 0x7f11a0002330
plugins/usbpro/UsbProWidgetDetector.cpp:537: Detected USB Device: ESTA Id: 0x0d6b (Shantea Controls), device Id: 0x0002 (OpenDeck), serial: 0x12345678, f/w version: N/A
plugins/usbpro/WidgetDetectorThread.cpp:324: Defaulting to a Usb Pro device
Fplugins/usbpro/WidgetDetectorThread.cpp:144: 1 are still active
plugins/usbpro/WidgetDetectorThread.cpp:148: 0x7f11a0002330
.common/thread/Thread.cpp:200: Thread , policy SCHED_OTHER, priority 0
plugins/usbpro/WidgetDetectorThread.cpp:399: trying stage 0 for 0x6e3d40
plugins/usbpro/UsbProWidgetDetector.cpp:323: USB Widget didn't respond to messages, esta id 0, device id 0
plugins/usbpro/UsbProWidgetDetector.cpp:326: Is device in USB Controller mode if it's a Goddard?
plugins/usbpro/WidgetDetectorThread.cpp:399: trying stage 1 for 0x6e3d40
plugins/usbpro/WidgetDetectorThread.cpp:396: no more detectors to try for  0x6e3d40
plugins/usbpro/WidgetDetectorThreadTest.cpp:142: Timeout triggered
.common/thread/Thread.cpp:200: Thread , policy SCHED_OTHER, priority 0
plugins/usbpro/WidgetDetectorThread.cpp:399: trying stage 0 for 0x6e4cf0
common/io/SelectServer.cpp:186: Removing an invalid file descriptor: 0x6e4cf0
plugins/usbpro/WidgetDetectorThreadTest.cpp:142: Timeout triggered
common/io/SelectServer.cpp:186: Removing an invalid file descriptor: 0x6e3d40


WidgetDetectorThreadTest.cpp:160:Assertion
Test name: WidgetDetectorThreadTest::testOpenDeckWidget
equality assertion failed
- Expected: 0
- Actual  : 3435

Failures !!!
Run: 11   Failure total: 1   Failures: 1   Errors: 0
FAIL plugins/usbpro/WidgetDetectorThreadTester (exit status: 1)

Also, I don't know what do \153, \15, \002 etc escape codes mean when defining manufacturer_data and device_data strings.

EDIT: Figured. Those are ESTA and device IDs. Fixed now.

@paradajz paradajz force-pushed the support_for_opendeck branch 3 times, most recently from 8babe5b to 771662a Compare October 23, 2021 14:51
Copy link
Member

@peternewman peternewman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some more tidying and comments/questions

plugins/usbpro/WidgetDetectorThreadTest.cpp Outdated Show resolved Hide resolved
plugins/usbpro/WidgetDetectorThreadTest.cpp Outdated Show resolved Hide resolved
plugins/usbpro/WidgetDetectorThreadTest.cpp Outdated Show resolved Hide resolved
plugins/usbpro/WidgetDetectorThreadTest.cpp Outdated Show resolved Hide resolved
plugins/usbpro/WidgetDetectorThread.cpp Outdated Show resolved Hide resolved
plugins/usbpro/UsbSerialPlugin.cpp Outdated Show resolved Hide resolved
reinterpret_cast<uint8_t*>(&full_frame),
length + 1);
} else {
return SendMessage(DMX_LABEL, &send_buffer[0], send_buffer.size());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You currently appear to be using the same label for your special diff packets as well as normal ones! How do you tell a normal DMX packet (which happens to have fewer than 512 slots) from a special packet?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the label byte comes data length LSB/MSB. Once I process those I know - if the size is 513 it's normal packet, otherwise it's diff mode.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately it's not as simple as that.

Try running this on your OLA machine:
./examples/ola_set_dmx -u 0 -d 10,20,30,40,50

Or using the pre-compiled one the debs ship or whatever. That will send a DMX frame with only 5 slots in it, which will confuse your diff thing I'm sure. The standard only mandates some timing restrictions on DMX packets, they don't have to have all 512 slots plus the start code. Pedantically my 5 slot example is probably too short to be valid, but something around 30-40 slots can easily be acceptable.

Your most compliant option is probably to pick another label just for yourself (like others have done, and send that with your diff format), rather than re-using the existing diff label with a different format.

As an aside, I'd recommend making sure your code is hardened to issues like this too, e.g. if I say the length is one thing, then don't send that much data, or if with your diff format I address a slot that's greater than 512, or send the channel number (or only half of it), but not the slot value etc.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh! I didn't know that's possible / valid. Okay. I'll implement new label. I've put it in OpenDeckWidget.h - not sure if that's the correct place.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And sure, I'll handle the edge cases on firmware side once all the issues in this MR are solved.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh! I didn't know that's possible / valid. Okay. I'll implement new label. I've put it in OpenDeckWidget.h - not sure if that's the correct place.

That matches what the other devices do. We should sort you out a login for our wiki so you can add some documentation on this format:
https://wiki.openlighting.org/index.php/USB_Protocol_Extensions

plugins/usbpro/OpenDeckDevice.cpp Show resolved Hide resolved
@paradajz paradajz force-pushed the support_for_opendeck branch 3 times, most recently from d9d2f6b to cd0633e Compare October 26, 2021 21:11
@paradajz
Copy link
Author

Had another look at this - not sure what to do next. Also don't know why the CI is failing, on my PC everything passes normally.

@paradajz
Copy link
Author

Can I get some update on this?

@peternewman
Copy link
Member

Sorry for not replying sooner @paradajz .

Had another look at this - not sure what to do next.

There are still a few outstanding comments (e.g. your own rate limiting key if you still want to do it). I'll see if the tests pass cleanly on a second run and try and re-review my outstanding comments and the code soon.

Also don't know why the CI is failing, on my PC everything passes normally.

I think that test has occasionally been a bit intermittent so it might clear with a re-run now you've fixed the broken test. I've (accidentally) updated the online version of your branch to match master, so you may need to pull on your development machine.

@paradajz
Copy link
Author

Thanks for the reply - I've rebased on master to remove the merge commit.

@paradajz
Copy link
Author

Hopefully no more changes are required. The CI is still failing but from what I see it's something completely unrelated (spell checker?).

@peternewman
Copy link
Member

Hopefully no more changes are required.

🤞

The CI is still failing but from what I see it's something completely unrelated (spell checker?).

If you want to cherry-pick this commit it will shut it up:
0556472

Copy link
Member

@peternewman peternewman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really sorry, just the one niggle with the token bucket that I think will be worth testing in the long term.

Comment on lines 112 to 117
if (m_bucket.GetToken(*m_wake_time)) {
return m_widget->SendDMX(buffer);
} else {
OLA_INFO << "Port rated limited, dropping frame";
}
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While it will make it a little more complicated, you probably want to move the rate limiting check to within OpenDeckWidget.cpp, otherwise you risk wasting your tokens on unchanged packets which don't actually get sent, rather than saving them for the first diff or full packet that arrives!

Depending on your processing times, you might also choose to take another token (I think you can) if processing one (e.g. unpacking the diff) takes longer than the other one on your device.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I've added initial, maybe naive approach. Let me know what you think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason you haven't just moved all the TokenBucket code to OpenDeckWidget, rather than keeping it outside and passing it in?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to do that, but to create TokenBucket, I need some parameters first, namely:

max_burst, rate, max_burst, *wake_time

These aren't accessible in OpenDeckWidget. I might be missing something here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be a bit beyond my understanding of OLA... Don't know what to do here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added simpler, alternative approach.


private:
static const size_t MAX_DIFF_CHANNELS = 128;
static const uint8_t DMX_SLOT_VALUE_DIFF_LABEL = 80;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Label 80 (aka 0x50) doesn't seem to appear in our codebase, so looks good to go! 🥳

Comment on lines 78 to 80
return SendMessage(DMX_SLOT_VALUE_DIFF_LABEL,
&send_buffer[0],
send_buffer.size());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix the whitespace:

Suggested change
return SendMessage(DMX_SLOT_VALUE_DIFF_LABEL,
&send_buffer[0],
send_buffer.size());
return SendMessage(DMX_SLOT_VALUE_DIFF_LABEL,
&send_buffer[0],
send_buffer.size());

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, pesky GitHub editor!

Comment on lines 112 to 117
if (m_bucket.GetToken(*m_wake_time)) {
return m_widget->SendDMX(buffer);
} else {
OLA_INFO << "Port rated limited, dropping frame";
}
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason you haven't just moved all the TokenBucket code to OpenDeckWidget, rather than keeping it outside and passing it in?

Copy link
Member

@peternewman peternewman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comments

@peternewman peternewman mentioned this pull request Mar 18, 2022
2 tasks
Comment on lines +42 to +44
bool OpenDeckWidget::IsBufferDiff(const DmxBuffer &buffer) {
return buffer != internal_buffer;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great minds! I literally just came here to write this after reviewing #1766 who's doing a very similar thing...

@paradajz
Copy link
Author

paradajz commented May 6, 2022

So is this ready for merging?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Sending DMX data to USB only if changed?
2 participants