Home AMX User Forum NetLinx Modules & Duet Modules

Learning how to do AMX modules

I'm new to AMX, but have been programming "the other guys" for 5 years. I have a job that is using a Biamp Nexia TC for audioconferencing. I need to learn how to make Netlinx modules, so since I programmed my own SIMPL+ Nexia conferencing module, I figured I would start here.

I an trying to use channels for input and feedback from the module, but I'm having trouble understanding how to send data like the current phone number back out of the module to the main. To add to my frustration, I've randomly downloaded some InConcert AMX modules to see what they did, only to inevitably discover that there isn't source code for the COMM portion. Have I just picked bad examples, or is this common for AMX-supplied modules?

Comments

  • CT-DallasCT-Dallas Posts: 157
    Gravity,
    AMX Supplied comm modules will not include source. The UI modules included with most AMX modules are open source, but are really intended to be used as a reference guide for creating your own UI module that works with your project code. Not sure if you are permissions limited because of certification status, but you should search the forums and you will find several examples of open source modules that other posters have contributed to the community.

    Good Luck!
  • jjamesjjames Posts: 2,908
    Gravity, first off - welcome to the forums.

    Some ways of sending data back and forth is through SEND_STRING and SEND_COMMAND; when I write a module, I used SEND_COMMAND to the virtual for actually commands, such as getting the power status (SEND_COMMAND vdvDevice,'POWER?'), and then within the COMM module, I'd do a SEND_STRING to the virtual for feedback (SEND_STRING vdvDevice, 'POWER=1'). Of course, in the COMM, there is no STRING event for vdvDevice, and in the UI module there is no COMMAND event for the virtual device.

    Of course, you could write a singular module where the UI and COMM is all in one file - easier, but a pain in my opinion and not the way of AMX standards. Also, you can use CHANNEL_EVENTs easily to do feedback and act upon things. Take this for example:

    1. UI module asks COMM module what the power status is.
    2. COMM module tells UI module what the power status is.
    3. COMM module turns a channel on within the virtual device (i.e ON[vdvDevice,255])
    4. UI module or main code has CHANNEL_EVENT[vdvDevice,255] and acts accordingly with it's ON or OFF sections.

    Of course, this is just one way to skin the cat . . . as I'm sure you know from experience, there's many ways to do a single thing.

    Hope this helps a little! Good luck!
  • CT-DallasCT-Dallas Posts: 157
    In all honesty, the forums will be your best source for code related questions. Many posters here will take your working or non-working code segment and show you how to improve upon it. As jjames suggests, you may get multiple replies showing different approaches.

    Here are some other links that contain different official documents on module creation:

    Module Example

    How to write your own Netlinx Module
    Adding functions to existing AMX modules using PASSTHRU
  • Thanks, guys. I'm guessing, then, that the best way to proceed would be to work on the COMM module first to put in the serial commands / responses I need, an the work on the UI module to interface with the main program?
  • jjamesjjames Posts: 2,908
    That's what I do . . . get the COMM mainly up and going, then tweek with both files open as new ideas pop up all the time and always changing until I get what I want.
  • Gotcha. Thanks for the help. I'll try to hammer through this today and post if I have any more detailed questions.
  • OK - possible dumb question:

    I plan on setting up the COMM module as a virtual device. In this module, how do I differentiate strings coming from the device versus strings coming from the UI module's "SEND_COMMAND"s?

    EDIT: Or is it most preferable to include 2 virtual devices in the Comm module - one for the device interaction, and another for the UI interaction?
  • jjamesjjames Posts: 2,908
    Typically in a COMM module you'll have two devices, one virtual and one real.

    Here's a flow chart I just made real quick.
  • That makes sense. Thanks.

    In my situation, I'm starting off with the dialer portion of the Nexia TC. Eventually, I'll add level and mute controls. Would it be best to have a Dialer UI and a separate Level UI that talk to the same Comm? If I do that, does the Master initiate multiple Comm instances, or is it smart enough to use only one instance of the Comm module?
  • jjamesjjames Posts: 2,908
    There should only be one comm module that talks to the real device (in this case your Nexia). COMM modules should handle all incoming and outgoing strings to the real device. UI modules should handle all actual visible feedback. Think of the COMM module just as an interpreter, a linkage to allow you to use a simple protocol (i.e. 'POWER ON') to relay a much more (at times) complex message for power on.

    I would have the dialer, level, whatever all in one module. The master sees however many modules you define - which should be one per real device. (Or, if you make it so - one per device set using arrays.) Unless you see an instance of only using the dialer, and no level control - you could have separate UI modules, but would not recommend it.
  • An important feature of the Comm module (and the reason you usually want just one) is to be able to control the interaction wtih the real device. You can build a command queue into the comm module and it will receive commands from a lot of different UI's or directly from a program, but it can time the data communications so you don't flood the real device.

    As you play with the AMX written modules you'll usually notice a 'Passthru' command. The Passthru command is written to let you send your own command to a module so it can be queued up and sent to the device. This is so that you don't throw commands at a device directly and risk sending data at the same time as the comm module and overflowing the device.

    Welcome to the forums by the way!
  • jjamesjjames Posts: 2,908
    Good bit of information there regarding the other importance of modules - queuing.
  • OK - I think I'm starting to get the hang of it.

    I am working on my COMM module, which currently defines a virtual UI device and a regular device for the DSP. I'd like to direct multiple UI modules to this one device - for instance, I more than likely will have multiple fader channels to control, and I would like to (for simplicity sake) put each on it's own virtual device. This will require calling an individual instance of the UI module for each, correct? Or is it preferred to create an array of virtual devices, and call the UI module instance once for each type (i.e., one Dialer UI and one Fader UI)?
  • ... and yet another question that I can't seem to find from the Netlinx Keyword Help file (is there a better place to look for documentation?):

    What are the advantages / disadvantages to using SEND_COMMAND between modules vs SEND_STRING?
  • jjamesjjames Posts: 2,908
    I am working on my COMM module, which currently defines a virtual UI device and a regular device for the DSP. I'd like to direct multiple UI modules to this one device - for instance, I more than likely will have multiple fader channels to control, and I would like to (for simplicity sake) put each on it's own virtual device. This will require calling an individual instance of the UI module for each, correct? Or is it preferred to create an array of virtual devices, and call the UI module instance once for each type (i.e., one Dialer UI and one Fader UI)?
    I'm actually kind of confused as to what you're trying to do. Could you explain a little bit more?

    If you look at modules written by AMX, you'll see that the COMM module has two parameters, the virtual and the real device; with the UI module you'll see multiple parameters, one of which is the same exact module defined in the COMM's parameters, and then sometimes an array of touch panels (or just one.) Think of the virtual that's defined in both as the interpreter as I'd said before - one guy handling all the information back and forth. Keep in mind UI modules are NOT required, they just make things a bit easier.
    ... and yet another question that I can't seem to find from the Netlinx Keyword Help file (is there a better place to look for documentation?):

    What are the advantages / disadvantages to using SEND_COMMAND between modules vs SEND_STRING?
    Review the flowchart a few posts up. You can write it however you want, but this is just a "standard" flow of modules. (Please someone chime in if I'm incorrect.)
  • truetrue Posts: 307
    On AMX devices, send_command sends a string to the device itself, and send_string sends a string through the device.

    In the comm/ui model use, with comms via a virtual device between modules, it really makes no difference. However, it allows for one to always be distinguished as a command to the device / comm module and the other to be the feedback from the device / comm module. You can do it separate but keep in mind that send_command or send_string will fire in BOTH modules - you can separate with send_command/string or do it by parsing, but the former is easier.
  • jjames wrote: »
    I'm actually kind of confused as to what you're trying to do. Could you explain a little bit more?

    Sure. Would I'd like to do is have (obviously) only 1 module talking to my Biamp - the Comm module.

    Different projects require different controls - and different quantities of those controls. On some, we may need to adjust 8 different level channels; on others, we may need to only do 1. The same goes for Dialers - we have up to 4 in a system, although we usually only have 1.

    I would like to have a generic "level channel" control, and another generic "dialer" control. That way, I can have each level channel and each dialer on it's own virtual device, and reuse the same channels / levels across the virtual devices to control the Biamp (e.g., channel 1 may be vol up, and channel 2 vol down across all the virtual devices). I'm looking to have the Comm module look at *which* virtual device is sending the message in order to direct the Biamp to change the appropriate level channel.

    Currently, the header of my Comm module looks like this:
    MODULE_NAME='Biamp_DSP_Comm'(DEV vdvDSP_UI[], DEV dvDSP)
    

    and my Main program calls it with:
    DEFINE_MODULE	'Biamp_DSP_Comm' modBiampDSPComm(vdvDSPArray,dvDSP)
    
    where vdvDSPArray is defined prior:
    vdvDSP			= 33333:1:0		//	Virtual Device for DSP Comm
    	vdvDSP_Fader1	= 33333:2:0		//	Virtual Device for Fader 1
    
    
            DEV vdvDSPArray[] =
    	{
    		vdvDSP,
    		vdvDSP_Fader1
    	}
    

    The Fader module looks like:
    MODULE_NAME='Biamp_Fader_UI'(DEV vdvFader, DEV vdvDSP)
    
    and is called with:
    DEFINE_MODULE	'Biamp_Fader_UI' modFader1(vdvDSP_Fader1,vdvDSP)
    

    The good news is that it appears to work at first blush, with one fader. But this leads me to another question. You can include variables in module definitions, but how do you define that module in the main program? I have tried:
    MODULE_NAME='Biamp_Fader_UI'(DEV vdvFader, DEV vdvDSP, INTEGER iUnitID)
    
    and
    DEFINE_MODULE	'Biamp_Fader_UI' modFader1(vdvDSP_Fader1,vdvDSP, iFader1Unit)
    
    where iFader1Unit is a constant value of 1, but I get the following error when I compile:
    ERROR: blah.axs(53): C10586: Module instance [MODFADER1] parameter [3] is an expression or a constant
    
    Is it possible to pass an integer in a module definition?

    EDIT: OK - I feel a little dumb. Assigning iFader1Unit using:
    INTEGER	iFader1Unit	= 1
    
    Under the variable definitions works fine.
  • jjamesjjames Posts: 2,908
    Is it possible to pass an integer in a module definition?
    Yes, make it a variable, not a constant.

    I don't see a problem with what you're doing as far as having multiple virtuals, it should work. You just obviously need to write the code in the COMM module to decipher them, but that'll easily be done with a GET_LAST.
  • OK - moving along here. I've run into a little bit of a speed bump, though. I am trying to put all of my Biamp internal devices into an array - the index of the array will be the port of the virtual device that calls it. I can't seem to decipher how to get the current port of a device. For some reason, no matter which module I call it in - either the UI or the Comm module, data.device.port returns the same thing: "1" if I call it in the Comm module (1 is the port number of my comm virtual device), and "2" if I call it from my UI module (2 is the port number of my *first* UI virtual device). Obviously, I can include an integer value in my variables in the main file, and pass the port number through the parameters, but I'd like to know what I'm doing wrong with this keyword.
  • truetrue Posts: 307
    What are 'internal devices?' Do you mean virtual devices? If so, I'm following...

    What does the index have to do with anything? You can use GET_LAST, or I wrote a function that checks a device against a dev array for a matching device to get the index. This way your array index doesn't have to match the port of the device you put in the array.

    What do you mean by "call" as in "module I call it in?" What are you 'calling?'

    I don't know your skills but I am guessing you fully understand events and the data_event handler, and this is where you are using the data.* struct?
  • By "internal devices", I'm referring to the fader channels, etc, in the Biamp unit.

    Currently, I'm using the device port as the array index for two reasons: I do not plan to have multiple devices with the same port number talking to this module, and I would otherwise need a serial numbering system anyway - may as well use the port number.

    By "call", I mean running the command. I "call" data.device.port in my data_event to determine (or to try to determine) the port of the device that sent the data.
  • truetrue Posts: 307
    Then I guess I'm not really sure what you mean by port numbers if you are referring to the Biamp devices unless you mean each of your "internal devices" modules has its own vdev. Is this right? I can see sort of why you are doing it this way coming from the Cresland...I worked with Biamp on a CP2E years ago... but you probably shouldn't do that - things usually aren't done that way - just use one vdev and use a number map. Comms to/from your comms module and your "internal devices" modules happens over one vdev. (Not saying you can't do it with multiple vdevs - some of my modules have their own vdev - but they still use the main vdev for comms communication and the comms module has no idea about the existence of this other vdev).

    How are you "calling" data.device.port? What "command?" I'm guessing you mean you are reading the value of it in a conditional or an assignment. Does your data event act on the entire array of vdevs? If you insist on using vdevs, try GET_LAST() or write an iterative match function to determine which device sent the data and see if that helps you.
  • My plan is to have each control (i.e., a fader channel or a dialer) as it's own vdv. i.e., if I have a four-channel fader at Biamp Instance # 10 or unit 1, I would have the COMM vdv and four fader vdvs:

    COMM: 33333:1:0
    fader 1: 33333:2:0
    fader 2: 33333:3:0
    fader 3: 33333:4:0
    fader 4: 33333:5:0

    What I can't seem to get configured is how to send the incoming value to the correct port only. For instance, if I get in the string:
    "'#GETLD 1 FDRLVL 10 2 999 +OK$0A'"
    
    I want whichever level I'm using for my volume to go to 999 on fader 2 (vdv port 3) *only*. If I use this in my Comm module when my fader UI modules connect:
    iCurrentPortNum = data.device.port
    
    iCurrentPortNum seems to always equal 1 or 2 - never any of the higher values.

    That said, currently I have the port number passing as a parameter to the fader UI module.

    I would like to keep each fader and each dialer on it's own vdv for scalability reasons. We have had large jobs with 4 dialers and 50+ faders, and it would be much more legible (IMO) to have each on it's own virtual device - for that matter, it would be difficult to fit all of the functionality on a single vdv.
  • My plan is to have each control (i.e., a fader channel or a dialer) as it's own vdv. i.e., if I have a four-channel fader at Biamp Instance # 10 or unit 1, I would have the COMM vdv and four fader vdvs:

    COMM: 33333:1:0
    fader 1: 33333:2:0
    fader 2: 33333:3:0
    fader 3: 33333:4:0
    fader 4: 33333:5:0

    I think you're going to gain a ton of knowledge by the time you're done with this project. Just to help out a bit with the terminology, What you described is actually only one virtual device (33333) with 5 ports (:1,:2,:3,:4,:5).

    A virtual device only has 1 port by default, if you want it to use more you have to explicitly define that using the SET_VIRTUAL_PORT_COUNT command.

    I think in this instance, instead of creating multiple ports for each fader within a device, you may want to consider using multiple "levels". That will cut down on your overhead a lot and I think it will accomplish the same goal.

    --John
  • Hmmm, still trying to figure out your methodology, but looking at one of your posts about channel 1 always being vol up, and channel 2 always being vol down I assume you're talking about buttons on the touchpanel and not a virtual device.

    If I understand correctly, since you're re-using the same channels on a generic fader UI page to control different devices or faders, somewhere in there you're going to need to track the state of the touchpanel as to which device it's trying to control. In other words, if you're controlling fader 3 on device 1, you're going to need to select device 1 on the touchpanel, then fader 3, then use the controls vol up/vol down. If my assumptions are correct, then you can just use a multi-dimensional array or map to track the actual level or device you're trying to adjust. Are these assumptions correct or am I totally missing it?

    --John
  • I agree that terminology is a big barrier right now - I'm trying to hammer my way through it, though.

    I get what you're saying about the multiple devices. I'll look at doing just the levels.

    When I posted about using the channels, I'd planned on having the module handle the ramping functionality. Looks like it may be easier to let the touchpanel handle that, or am I thinking wrong here?

    I'm changing my modules to only use different ports on a vdv (see - I'm learning ;) ) if there are different instance or unit numbers on the different fader controls.
  • ...and the frustration continues.

    My biggest concern right now is how in the heck do I direct strings from my comm module back to the correct device for the proper UI module? I know that I will need at least two UI modules - one for faders and another for a dialer. If I try a [virtual device].number in my comm module, the value is 5. Where the heck would 5 come from? My device number is 33334!

    When you have multiple modules interfacing with one other, how is the common module supposed to direct it's responses to the correct virtual device?
  • Jorde_VJorde_V Posts: 393
    ...and the frustration continues.

    My biggest concern right now is how in the heck do I direct strings from my comm module back to the correct device for the proper UI module? I know that I will need at least two UI modules - one for faders and another for a dialer. If I try a [virtual device].number in my comm module, the value is 5. Where the heck would 5 come from? My device number is 33334!

    [virtual device].device.number

    Things can be a little frustrating at first.
    When you have multiple modules interfacing with one other, how is the common module supposed to direct it's responses to the correct virtual device?

    You know where the response came from right? You send it tot he correct module from where you got the response.
Sign In or Register to comment.