I noticed, that there is no real prioritization, when it comes to sources of motor commands input in current VESC implementation.
I started adapting my e-bike to use VESC, but it quickly occurred to me, that it won't be an easy task given my requirements (PAS Assist + throttle + regenerative braking).
My bike is equipped with a 3 wire PAS sensor which doesn't seem to be supported by VESC out of the box (it seems that only quadrature PAS sensors - 4 wire, are). Not wanting to change the hardware, I decided to create a small arduino board, that would read my PAS sensor input and generate proper motor commands to VESC over CAN-bus. Unfortunately, it quickly occurred to me, that this isn't working, as ADC app input (which throttle was connected to) generates motor commands even when its' value is "zero", therefore overriding, or rather - conflicting, with any motor commands, that VESC receives over CAN-bus. As a quick fix, I changed the logic of ADC app to not send any subsequent "0 current" commands, after sending the first one. This way, the ADC would still have priority over CAN (not really, as CAN commands would still be executed, just as a significantly slower rate, than ADC ones), but would at least not interfere, when idle.
This wasn't ideal, so I though about adding another arduino board for the throttle input, but this would still not solve the issue, as CAN commands from PAS sensor would "conflict" with CAN commands from the throttle, rendering useless results.
I therefore started thinking about implementing motor input prioritization, where each potential source of motor commands would have its' priority, and the messages/commands from all those sources would not drive the motor directly, but, instead, join a queue with it's own priority and Time To Live (TTL), and there would be a separate thread ("dispatcher", although this name isn't perfect) constantly monitoring this queue and executing commands as per their priority and their validity period.
There would be a Motor Input Priority Configuration array containing:
1) Source ID (eg. fixed IDs for hardware inputs, like 0x01 for ADC1, 0x02 for ADC2, 0x03 for ... and then configurable IDs let's say from 0x32 (50) up to 0xFF, for "digital" type devices, like CAN-BUS or UART).
2) (optional) Source Type (HW PIN, UART, CAN)
3) Command Type (Current Set, Duty Cycle Set, Braking Current set, eRPM set, etc.) - this way one source could have different priorities depending if it's asking for acceleration, of braking, or duty eRPM etc.
4) Time To Live (TTL) - how long, in ms, last command from the source should remain active, before it is considered stale, and next entry with lower priority is executed from the command queue.
5) (optional) Priority - instead of this, we could use the Array Index as priority, for. eg. lower - higher priority and I think the latter is cleaner.
This Array would be configurable from VESC tool, over UART, CAN etc.
Motor command dispatching:
There would be a Motor Command Queue array, containing following fields:
1) Command Type (Current Set, Duty Cycle Set, etc.)
2) Command Value (for. eg. Current to be set, duty to be set etc.)
3) When arrived (in arduino that would be a millis() "timestamp" of when the command arrived to the queue).
4) (Optionally, so that dispatcher wouldn't need to seek for this info elsewhere) TTL of the command, taken from Input Priority Configuration array.
5) (Optionally) command source ID
6) (Optionally) command source Type
The latter two would only be probably needed for logging etc.
Motor Command Dispatcher:
A separate "dispatcher" thread, being a heart of the whole solution, would be running and iterating over the Motor Command Queue array continuously (period of those checks can be configurable, but the more often, the more responsive the controller would be, however other threads could suffer from this). It would take it from the top (or actually bottom - index = 0) and continue iterating until it finds a command, that is within it's TTL (again, in Arduino world - "millis() - "When arrived" <= TTL") and would execute this command by calling proper MC_INTERFACE function, that would actually drive a motor.
Then it would go back to the beginning and repeat.
Motor Command Queuing:
Each function that currently drives the MC_INTERFACES directly (APPs, CAN_COM, UART_COM etc.) would now, instead, call a special Motor Command Queuing function. That function would take "Source ID", "Command Type", "Command Value" as arguments and check those arguments against the Motor Input Priority Configuration array, and put the requested command into the Motor Command Queue under an appropriate index, adequate to the Source's and command's priority, overwriting whatever is there now (previous commands from the same source and command type). It would also maintain "When arrived" under that index with a current "millis()" and TTL from the Motor Input Priority Configuration array for dispatcher's use. If no ID is provided when calling Motor Command Queing function, command would land at the bottom of the queue with some default TTL. We could have separate indexes at the bottom of the queue for each Command Type, so that, for eg. Braking commands would always land higher than acceleration commands, but this could still be configurable, including TTL.
Hardware interfaces (ADC, PPM etc.) would have fixed IDs, as mentioned earlier.
CAN devices could add its' ID to the B24-B16 bits of extended ID, that are currently unused, or to the CAN Packet itself, for eg. in the first byte. In the latter case, the currently used Bytes 0-3 would need to be shifted one byte to the right. If B0 would be 0x00, then we would assume "default ID" and therefore - end of the queue.
Not sure how UART comm works - didn't look into that too much yet.
Each potential source of motor commands (PPM remote, ADC, CAN or UART device) would have it's own, configurable priority depending on the type of command they issue, making the setup very versatile with many applications easy to implement (CAN alarm devices, that would drive the motor and take precedence over other inputs, autopilots, cruise control etc.)
That is what I came up with, for now. I think, this would add a layer of flexibility to the VESC firmware. I could (and probably will) start coding this myself, but I'm not very familiar with the VESC firmware code, nor the Chibi OS yet, and I'm not a great C programmer either, so it will probably take some time before my code starts working, and, for sure, even more time, before it becomes reliable, if at all, so I wouldn't share it with anyone until I'm 100% sure, it's good (and, since I'm a perfectionist, it would take even longer). Sharing it is another issue, as I don't know GIT very much. I'm also not sure, how this would impact VESC features, that I don't consider as useful in my use case, like Traction Control etc. so it might be, that this what I mentioned above, cannot be implemented the way I suggested, as it would render other features not working. Perhaps someone with greater familiarity with the VESC firmware code, would be able to verify that.
I therefore decided to share this idea here in hope that it will become a seed that will find a fertile ground here that will develop into a feature (or set of features) that will make the VESC firmware even more flexible, than it is now.
EDIT: minor corrections, added "result" section.