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
i2c chaining #5
Comments
See issue #53 for discussion of auto-discovery & indexing of units in a multi-crow setup. |
@tehn thinking about syntax for the basic i/o expander setup (ie one crow is leader, and uses extra crows for adding ins & outs: -- direct i2c call style (currently {secretly} supported)
ii.crow.output(1,2.3) -- set remote crow's first output to 2.3 volts
-- can be extended to multiple devices with forthcoming multi-device support
ii.crow[2].output(1,2.3) -- as above
ii.crow[3].output(1,3.4) -- a third crow's first output is set to 3.4 volts
-- we could use the input/output metamethods to implement:
output[1].volts = 1.1 -- regular old output 1 to 1.1volts
output[5].volts = 2.2 -- forwards to second device over i2c
output[5].slew = 0.1 -- slew is also a default supported message
-- this breaks down when you try and use asl
output[5].action = ar() -- will not work
-- for inputs:
ii.crow.get('input')
-- triggers ->
ii.crow.event(e,data)
if e == 'input' then
-- do things
end
end
-- this could be extended to allow something like the stream/change events
input[3].mode('stream',0.1) -- note: no event supported. must use the default which will be i2c comm'n
-- triggers another added function
input[3].stream(d)
-- do things with the value sent over i2c
end This last idea with inputs seems really nice, but involves a fair bit of magic. I'm not opposed to that, but don't want to jump into implementing this stuff unless it seems like the appropriate path forward. The indexing of the input & output libs seems nice, but it's a little tough because inputs are %2 and outputs are %4, so it's not immediately obvious which device you're communicating with. I guess we should be asking, what are people trying to get out of a multi-crow setup in the first place? |
i like all of these ideas-- follows the way i was thinking TT expands via ansible(s). for the use as a norns/max expander, having two crows side-by-side means it's not too weird to have the ins/outs count up a different rates (2/4). not sure if you're suggesting implementing both ie curious what the performance/throughput will look like. might be worth considering ahead of too ambitious of a design |
Currently Throughput is an interesting question. I've had 2 crows talking back and forth and i think it was roughly 1000 request/responses per second. That's probably 4~5bytes per side, so in the 4-5kB/s. Mind you, that's happening via the event system on both crow's, so that's simultaneous to outputs being generated etc. Not enough for streaming audio, but certainly fine for streaming a few continuous cv channels i think. (Agreed more thorough testing is an important thing here though!) |
I've been using two crows as input/output modules for norns for a bit now. In this setup I'm using the second crow simply as an IO extension where I'd like to use the same functionality as on the primary crow. Being able to set pitch/voltage over ii is already possible but there's no way to easily trigger gates or envelopes. I think there are two or maybe three approaches to this: Add some simplified functionality, similar to the ii methods that are available for Ansible/Kria, use/abuse the callN functions to do this or try to make a generic thing that would allow executing anything including ASL. I don't like the second option because it means I need to write a matching script for the second crow whereas with the other two solutions I can just use it as-is. And I'm not sure the last option is achievable. What I've done so far is use the second crow that's connected over ii purely for pitch/voltage output and handle all the gates on the primary crow. P.S. It's not directly related to this, but ii in general can serve as an alternative for requiring more IO because instead of having to for example send out a gate and pitch voltage that information can be sent over ii freeing up two output channels on crow. The ii ecosystem is missing sound sources though to make this a viable option. AFAIK there are only Just Friends, W/ and the ER-301 and TXo in oscillator mode, although I believe the latter one is a bit limited in functionality. |
I'll try and add some of my perspective to the 3 options. I know you've probably already thought about this ideas, but just fleshing them out so we have real code to look at and hopefully further inform a direction goal. Add baked in ii follower commands ala AnsibleThe clear benefit is ease of use and more idiomatic Main questions are which additional commands we want to see? For your use case I'd imagine: ii.crow.output[1].pulse()
ii.crow.output[2].ar()
ii.crow.output[3].lfo() These are 3 basic ones to start. Would they be fully parameterized like the I like this approach as an end point for ii support, not as a jumping off point. It's not a sustainable way to allow inter-device access to the whole asl syntax (just look at the ridiculous magic we had to add to make norns able to speak to crow, not to mention ii!). Use callNThis is the most clunky option for sure, but it's also the most flexible. You can do pretty much anything this way and therein lies the problem (no universal system is ever elegant for every usage). One workaround to maintaining multiple scripts is to wrap all the working code in conditionals based on the ii address. This gets weird if you want your followers to do more complicated things with events, but that seems not to be the case for you. function init()
local myaddress = ii.get_address()
if myaddress == 1 then
-- leader init
-- setup timers etc that calls ii.crow.callN()
else
-- follower init
end
end
ii.self.call1(arg)
-- will only be called on the follower device
end A second part could be to have manual --- norns calls `crow.send("myinit()")` on script start
function myinit()
ii.crow.call1(0) -- inits the attached crow
-- leader device init goes here >>
end
ii.self.call1 = function(arg)
-- follower device init goes here
end This feels awkward, but has the benefit of only requiring a single script that can run on both devices. Extend
|
@trentgill Thanks for sharing your thoughts on this. From my current understand I agree with your final assessment that the first option is probably the way to go, especially for now/the short term. It's achievable, we have some existing examples we can follow and it would already enable a bunch of use-cases which could provide valuable input for potentially a better system that supports arbitrary code chunks. Regarding syntax, personally I think I prefer Regarding the functions to expose: I think we can look at the existing functionality of other ii devices as well as crow's own API and choose what we prefer.
Maybe prefixing the
That's a good question, I'm not sure tbh. It would of course be very nice, but I'd expect it would also be quiet a bit more work, so maybe for the initial implementation they should be fixed? This way we can get some experience and learnings and go from there. Wall of text following below about the crow and ii plumbing code in nornsRegarding the plumbing code that is required to make crow and ii devices work on norns I think that's just a consequence of some high-level design choices. I.e. ii defines just a message format, not it's contents, so devices can and will have different names for their functions (I'm not sure if functions is the correct word in the ii context, but I hope you understand what I mean). On top of that there's no discovery mechanism defined in ii for these functions so then you're effectively left with two options: have a single send-over-ii function that simply sends the data over i2c as-is or provide some convenience wrappers which are effectively a copy of the API) to make the experience easier/better for the user, which is what we currently have. I think for crow the issue is sort of similar in that the way it works is by sending it strings of text which then get executed on the device. Here we have the same issue on norns (or in druid, etc) that we don't know which functionality is available so we end up with the same choice of adding convenience wrappers effectively duplicating the whole API or some discovery mechanism. On crow at least some (human focused) discovery mechanism is present making using druid a bit easier of course. Hope this makes sense :) |
The second option was actually a much deeper shortcut: The idea being that outputs >= 5 are accessed via Now that I look at the current scheme, we're already breaking from the native crow code, requiring input/output channel to be sent as the first argument (not as a table index). Below is the definitions copied from here: -- actions. shouldn't return a value
ii.crow.output(chan,val)
ii.crow.slew(chan,slew)
ii.crow.call1(arg)
ii.crow.call2(a,a2)
ii.crow.call3(a,a2,a3)
ii.crow.call4(a,a2,a3,a4)
-- queries. must return 1 value
ii.crow.get('input',chan)
ii.crow.get('output',chan)
ii.crow.get('query0')
ii.crow.get('query1',a)
ii.crow.get('query2',a,a2)
ii.crow.get('query3',a,a2,a3) If you could write out your desired functionality & syntax for this extension, I can add it to my list.
This I'm less worried about. Typically the cv/tr dichotomy is a hardware one, so tr(1) and cv(1) refer to different physical jacks. In crow's case it's a single physical jack, so it seems more that the 'cv' or 'tr' would refer to the action of the command than the hardware (which is an awkward difference imo). Using |
Ah, I missed that. FWIW I definitely prefer the
You're totally right, they are all distinct output types and thus prefixed by their type. That doesn't apply to crow of course because all outputs are equal so there's no need for that distinction. I guess we can use the verbs from the existing ii devices without the
Is this something we should/you'd like to change? All the actions for all devices (not only crow) listed in https://github.com/monome/norns/blob/b1395713cf7d81fd727fae57f68a81792657c798/lua/core/crow/ii_actions.lua work this way it seems.
Taking the existing actions without a prefix as starting point, if we start with Ansible's actions (most other devices which support both CV and trigger outputs have similar names for their actions) we have this list:
Removing the
I guess we'll have to choose which one of these makes sense. Other things like LFOs and envelopes seem to be only supported by TXo (see https://github.com/monome/norns/blob/b1395713cf7d81fd727fae57f68a81792657c798/lua/core/crow/ii_actions.lua#L217 and https://github.com/monome/norns/blob/b1395713cf7d81fd727fae57f68a81792657c798/lua/core/crow/ii_actions.lua#L199) so there's less of a precedent on how to handle them. One interesting thing to note is that all ii devices use |
i'm in preference of i am not particularly enthusiastic about implementing an ii "pipe" to send lua code between crows. i would highly consider this to be a completely separate feature, as currently ii does not have a string implementation. will comment on the other issue re: finalizing the syntax |
For anyone else interested, the syntax discussion has been moved here #258 |
#258 remains open, but closing this as it's pretty well implemented already and will be 'complete' with 258. |
LATER cross-crow commands ie send lua from one crow to anotherie:
cv(5, value)
and cv command is passed via i2c to crow#2 (which has cv position 5)The text was updated successfully, but these errors were encountered: