Home AMX User Forum NetLinx Studio

TCP communications howto

Hi folks,

I have some embedded devices that can be controlled via telnet, and I'd like to control them from my Netlinx master (& receive info back). I've never done this before (only having recently transitioned from Axcess), so would appreciate any advice, tips & tricks, or pointers to tutorials.

Thank you!

Comments

  • jeffacojeffaco Posts: 121
    Audiotron, SyslogMod, and TimeSync are all working examples of code that do TCP/IP communications.

    They're open source and up on SourceForge:

    http://cvs.sourceforge.net/viewcvs.py/netlinx-modules/NetLinx-Modules/

    Hope this helps,

    -- Jeff
  • Thanks Jeff. Actually, I use your SyslogMod already, and it's extremely nice. I didn't think to look in there. :)

    Do I just use ip_client_open to port 23 on the remote machine? Does it matter which outgoing port I use (since I'd like to open a bunch of these, is there any specific block I ought to use? Does the NI-3000 support the full 65k some odd ports)?
  • In general, when the connection is established on IP level, you may send and receive any data, depending on the protocol and data handling of the device you want to control.

    Find attached an example for a Sony SNC-RZ30 professional WebCam. this unit is controlled by HTTP.

    Also included are two more examples, one for the NetLinx as a Client and one for NetLinx as Server.
  • TCP communications howto

    In response to your specific questions, yes, you would use IP_CLIENT_OPEN() with a target IP address of your desired server and with port 23 as the desired port. If your server requires authentication as part of the connection sequence, you will have to include that as part of your ONLINE handling for when the connection is first made.

    Regarding outbound ports, let's be clear on the nomenclature. You will need a unique Netlinx device for each server connection you wish to make. Netlinx/VxWorks will map the device onto what is commonly referred to as an IP port (this is visible using TCP List and IP Status in Netlinx terminal for your information but it is handled for you so you do not need to worry about it). The outbound port when connecting to another server is determined for you by Netlinx/VxWorks. The server port (which is listened on by the target server) was specified by you in the IP_CLIENT_OPEN() call. You should use a sequential range of Netlinx devices for your connections since Netlinx allocates memory to handle connection management up to the largest defined device. Therefore, if you decide to use a block of devices leaving a large group of devices unused, memory for them is still allocated in Netlinx. Start your devices at 0:2:0 for example and define them consecutively. Be aware that if you are using some i! Internet Inside modules, they assume the use of certain TCP devices so be sure not to conflict on allocation.

    Take a look at IP_SERVER_OPEN() to get an idea of how you would create a server that would listen on a specific port for connections from other clients. This may also help you understand better what your telnet device is doing and how it will interact with your client.

    Lastly, regarding your question on port support in the NI-3000, I am not sure if you are referring to the entire IP space of port numbers or a Netlinx range of port numbers. The NI-3000 should be no different than any other Master in terms of port support provided you are comparing Masters on the same firmware level. I have used target IP ports in the 2000 range for DigiOne servers and in the 3000 range for ReQuest servers. I have rarely had to use a port larger than those. I know that the server port value is declared as a LONG and hence large port numbers can be specified even though an unsigned INTEGER would handle up to 65k. I do not know the largest port number that Netlinx can handle but I suspect Netlinx/VxWorks will handle any server port you require for your application.

    In regards to maximum TCP connections, you can have at most 200 open IP connections according to the last documentation I read. Hopefully this won't be a problem for you :). Hope this helps,

    Reese
  • jeffacojeffaco Posts: 121
    Thanks Jeff. Actually, I use your SyslogMod already, and it's extremely nice. I didn't think to look in there.

    Do I just use ip_client_open to port 23 on the remote machine? Does it matter which outgoing port I use (since I'd like to open a bunch of these, is there any specific block I ought to use? Does the NI-3000 support the full 65k some odd ports)?

    I think Reese did an excellent job of beating me to the punch, and answering your questions.

    If you have further questions, just ask. It's actually pretty easy to use TCP/IP communications on NetLinx, and fairly trouble free (depending on the device that you're connecting to).
  • I think Reese did an excellent job of beating me to the punch, and answering your questions.
    Agreed! Thanks Reese, Marc & Jeff. This makes perfect sense now.

    One last question: Other than Jeff's Sourceforge site, where can I find other Netlinx module repositories? I'll be writing a module over the next few days to control Baytech RPC series ethernet power switching units, and would like to give this work back to the community. (Jeff, I'll contact you via email about putting it on your site when it's done)
  • GAH!!!

    I am going insane. I need some more advice. It doesn't seem to work right... ip_client_open is returning 0 (success) no matter what. I don't know what I'm doing wrong. It worked randomly a couple of times, but 90% of the time it just returns 0 and nothing happens.

    BTW, I put everything in mainline to remove any module problems, and will turn it into a module once I get it working.

    Here's the relevant code:
    DEFINE_DEVICE
        vdev_BayTechRPC3_1		= 32772:1:0
        net_BayTechRPC3_1		= 0:12:0;
    
    DEFINE_CONSTANT
        char BayTechRPC3_1ip[]	= '192.168.1.40';
    
    DEFINE_VARIABLE
        volatile sinteger result
    
    DEFINE_START
    
    DEFINE_EVENT
    
    channel_event [vdev_BayTechRPC3_1, 1] {
        on: {
        
    	syslog(Info, 'BaytechRPCModule', 'Attemping to open socket')
    
    	result = ip_client_open (net_BayTechRPC3_1.Port,BayTechRPC3_1ip,23,1)
    
    	syslog(Info, 'BaytechRPCModule', "'Result code = ',itoa(result)")
    
    	wait 20 { 
    	    syslog(Info, 'BaytechRPCModule', 'Send first command')
    	    send_string net_BayTechRPC3_1, "'1',13" 
    	}
    	    
    	wait 50 { 
    	    syslog(Info, 'BaytechRPCModule', 'Send second command')	
    	    send_string net_BayTechRPC3_1, "'ON 1',13" 
    	}
    	wait 60 { ip_client_close (net_BayTechRPC3_1.port)
    	    syslog(Info, 'BaytechRPCModule', 'Closed connection')
    	}
    	
        }
    }
    
    data_event [net_BayTechRPC3_1] {
        string: { 
    	syslog(Info, 'BaytechRPCModule', "'data: ',data.text");
        }
    }
    

    When I manually turn on the virtual device I get the following in syslog:

    Feb 19 14:56:00 ni3000 BaytechRPCModule: Attemping to open socket
    Feb 19 14:56:00 ni3000 BaytechRPCModule: Result code = 0
    Feb 19 14:56:02 ni3000 BaytechRPCModule: Send first command
    Feb 19 14:56:05 ni3000 BaytechRPCModule: Send second command
    Feb 19 14:56:06 ni3000 BaytechRPCModule: Closed connection

    For kicks I unplugged the network connection to the RPC device I'm trying to control. ip_client_open returned 0 anyway! Obviously this is false.

    Originally I had "net_BayTechRPC3_1 = 0:1:0;" and so I changed that to "0:12:0" just for fun and rebooted. It worked great - logged into the RPC unit and flipped the outlet on like it was supposed to. But it only worked once! Now it doesn't work at all again no matter what I do (yes, I manually turned off the channel on the virtual device, the channel_event is executing and logging to syslog, but the ip connection isn't working at all).

    I'm lost. Netlinx seems very unpredictable to me. I never had these problems with Axcess, either something worked or it didn't, every time. Things in Netlinx often fluctuate between working and not for me, even with zero changes to code. I don't get it.

    Thanks for any advice.
  • TCP communications howto

    First, recall from the tutorial earlier in this thread that your TCP device should start at 0:2:0 or higher. Device 0:1:0 is the Master and is not going to work for TCP communications as you discovered.

    Second, the code block you provided should work in most cases although I prefer to send data to the device only after I receive the ONLINE event for the connection to the server. You have chosen to send data to the device following the IP_CLIENT_OPEN() after a small delay and this will most likely work for most devices most of the time (see caveat below).

    Third, to your problem of repeatability, you said that it worked once but not again after that successful connection and command control. It is possible that the BayTech is not dropping its server connection correctly in response to your IP_CLIENT_CLOSE() and therefore you are able to connect again but it does not process commands. I would try as an experiment resetting the BayTech device and then re-trying the connection and control program to see if it works. I can not explain why you would be able to connect to the BayTech at the IP address if you have taken the device online. I don't believe that IP_CLIENT_OPEN() will be successful if the server is not on the network but since I code all of my modules to check the return status of the IP_CLIENT_OPEN() and and to wait for the ONLINE event to ensure the connection was made, I can't comment with certainty. See my recommendation below so we can determine if this is an issue with your device.

    Lastly, I have been using TCP communications within Netlinx for a variety of devices both native TCP and through RS232-TCP converter devices. I have had reliable and consistent results with TCP communications so I know that it is possible for you to achieve the same provided the device you are attempting to communicate with is also well behaved. I have used TCP with Lutron, Extron, Denon, Escient, ReQuest, Panasonic IP cameras, and other devices all with tremendous success. I prefer TCP in fact to serial communication with the same device wherever possible. In other words, don't give up. Reliable TCP communication is possible - we may just need some tweaks to your Netlinx code and some better understanding of how the BayTech device works.

    Try resetting the BayTech and let's take it from there. If you are game, create yourself an ONLINE event for the TCP device and then send your command strings to the device under that event instead of following the IP_CLIENT_OPEN(). Leave the client open where it is but move the other code, add your logging to the ONLINE event, and let's take it from there. Add an OFFLINE handler to the same DATA_EVENT to ensure the connection is broken on the IP_CLIENT_CLOSE() and add some logging to that effect. Some devices (and the BayTech may be one of these) can report whether or not a network connection is open to it. If you have a console interface to the device and the interface will provide connection status to you, it would be interesting to check the status when you are connected and disconnected via TCP.

    Reese
  • Reese,

    Greatly appreciate the reply and your (& everyone else's) infinite patience here.

    Resetting the Baytech didn't help, but for whatever reason, adding online & offline event handlers did. It now works perfectly, every time. Of course, there is still a lot of work to do - verifying the Baytech's response, setting up a command queue, etc - but I am well on my way.

    I do have a new problem, however; the module is not working. I moved my code over exactly. It just sits and returns 0 as a result code but the Baytech's interface shows no users logged on. I moved the code back into mainline and it worked perfectly.

    Here is the module (comments removed):
    MODULE_NAME='BaytechRPC' (dev net_BayTechRPC3_1, char BayTechRPC3_1ip[], dev vdev_BayTechRPC3_1, dev vdSyslog)
    
    #include 'SyslogMod.axi';
    
    DEFINE_CONSTANT
        char Info = 6;
    
    DEFINE_VARIABLE
        sinteger result
        char rpcbuffer[1024]
    
    DEFINE_START
        create_buffer net_BayTechRPC3_1, rpcbuffer
    
    DEFINE_EVENT
    
    channel_event [vdev_BayTechRPC3_1, 1] {
        on: {    
    	syslog(Info, 'BaytechRPCModule', 'Attemping to open socket')
    	result = ip_client_open (net_BayTechRPC3_1.Port,BayTechRPC3_1ip,23,1)
    	syslog(Info, 'BaytechRPCModule', "'Result code = ',itoa(result)")
    	
        }
    }
    
    data_event [net_BayTechRPC3_1] {
        online: {
    	syslog(Info, 'BaytechRPCModule', 'Device reported online')
    	
    	wait 10 { 
    	    syslog(Info, 'BaytechRPCModule', 'Send first command')
    	    send_string net_BayTechRPC3_1, "'1',13" 
    	}	    
    	wait 20 { 
    	    syslog(Info, 'BaytechRPCModule', 'Send second command')	
    	    send_string net_BayTechRPC3_1, "'ON 1',13" 
    	}
    	wait 100 { ip_client_close (net_BayTechRPC3_1.port)
    	    syslog(Info, 'BaytechRPCModule', 'Closed connection')
    	}
            
        }
        
        offline: {
    	syslog(Info, 'BaytechRPCModule', 'Device reported offline')    
        }
        
        string: { 
    	syslog(Info, 'BaytechRPCModule', "'data: ',rpcbuffer")
        }
    }
    
    DEFINE_PROGRAM
    

    (btw, I intend to use more descriptive or "proper" variable names in the module, but left them identical to the main code to ensure no errors were introduced when pasting back and forth for testing)

    And the main program (relevant code only):
    DEFINE_DEVICE
        vdev_BayTechRPC3_1		= 32772:1:0
        net_BayTechRPC3_1		= 0:12:0;
    
    DEFINE_CONSTANT
        char BayTechRPC3_1ip[]	= '192.168.1.40';
    
    DEFINE_START
     // at bottom of this section:
    DEFINE_MODULE 'BaytechRPC' modBaytechRPC3_1(net_BayTechRPC3_1,BayTechRPC3_1ip[],vdev_BayTechRPC3_1,vdSyslog);
    
    
  • TCP communications howto

    Ok, sounds like you are making some good progress. Glad to hear (prior to the module conversion) that the code now works quite reliably. I knew you would be a convert to TCP once we got it working for you :).

    On the second problem, the module conversion. The only obvious thing that I see is that you have declared the IP address string in Main as a CONSTANT. Module parameters can not be constants so the 1st thing to change would be to move the string declaration of the IP address to the DEFINE_VARIABLE section and make it VOLATILE. It appears that you have placed the DEFINE_MODULE between the DEFINE_START and DEFINE_EVENT sections so this is correct. Let's see if the IP address string is the problem. Since it was not logged as part of the syslog during the IP_CLIENT_OPEN(), we don't know for sure if the IP address was valid.

    One other observation - you created a buffer (1024 characters in length) for the responses from the device and you are logging responses in the STRING handler. I did not see that you were doing a CLEAR_BUFFER() anywhere so you are going to end up logging a large block of accumulated strings over time. You might want to clear the buffer (multiple ways to do this) in the STRING handler once you have logged the data. You said you had some more tweaking to do on the module but I did want to make that note in case it was not on your list.

    Hope this helps --


    Reese
  • Fixed it. I didn't even think to pass the IP address to syslog to see if the module was getting it OK. I did that and realized it wasn't. Changed it to a variable and still no luck. Here's what did the trick - In main code, I changed:

    DEFINE_MODULE 'BaytechRPC' modBaytechRPC3_1(net_BayTechRPC3_1,BayTechRPC3_1ip[],vdev_BayTechRPC3_1,vdSyslog);

    to:

    DEFINE_MODULE 'BaytechRPC' modBaytechRPC3_1(net_BayTechRPC3_1,BayTechRPC3_1ip,vdev_BayTechRPC3_1,vdSyslog);

    (removed the brackets after BayTechRPC3_1ip).

    Works like a charm. Now I can get to work coding the rest of the module.

    Again, thank you very much for your help.
  • TCP communications howto

    Well, mea culpa, for some reason I did not see the [] brackets on the declaration passing the string to the module. That would indeed be a problem. You do not use the brackets on the module invocation but you do need them in the MODULE header itself to let the module know the argument is a string. This combined with having the string be a variable does the trick as you noted.

    That is good news - enjoy reliable TCP communication.

    Reese
  • Another 2 cents...

    One thing I like to do in TCP/IP code is have the online and offline events set and clear a flag.

    But why, Chip?

    I found a long time ago - and this may have changed with newer revs of the firmware - that if I was trying to send strings out a TCP/IP port that didn't/wasn't open, I would very often get quirky behavior on the master, like unexpected, random lock-ups & such.

    With the flag, I had all my outbound strings go to a "Send To TCP Socket" function, which just checked to make sure the flag was set before trying to send anything out...

    Your mileage may vary..

    - Chip
  • Joe HebertJoe Hebert Posts: 2,159
    Originally posted by Chip Moody
    One thing I like to do in TCP/IP code is have the online and offline events set and clear a flag.

    I do the same and I make sure to reset the flag in the ONERROR event also.

    I think AMX did an excellent job with their TCP/IP implementation and I find it to be extremely reliable.

    I have an Internet application that I wrote which opens two dozen ports at startup that grabs over 100 files and runs various scripts from several web servers, does heavy disc I/O, takes advantage of Dynamic Images, and Netlinx never misses a beat while doing it all in a matter of seconds.

    I keep adding features to the program wondering if I'll eventually overload it but Netlinx hasn't failed me yet. I'm tempted to port the same application over to the bloated world of Mr. Bill but I know it will be no where near as easy to do or even a fraction as reliable.

    Two thumbs way up to AMX, Netlinx, and IP! :)

    Joe
  • Originally posted by Joe Hebert
    Two thumbs way up to AMX, Netlinx, and IP! :)

    Agreed. Aside from working great, AMX's implementation for IP-based communications is loads better than the current offering for that other popular control system in a few ways... Like, you have to define a symbol (think of it as needing to define a 0:x:0 device in Netlinx) for EACH connection you want to make and provide STATIC address & port settings. I.E., if your program got an IP address and/or port during run-time, you wouldn't be able to open a connection to it. You can ONLY access those that you specify pre-compiling... :(

    - Chip
  • One thing I like to do in TCP/IP code is have the online and offline events set and clear a flag.
    That's funny - I've already set that up in my final code as well, now that I got the test code working. Not for the reasons you specified (I didn't know about them) but to make it easier to know when it's OK to call certain functions. Thanks for the info, I will make sure to always do this in the future as well.
    I think AMX did an excellent job with their TCP/IP implementation and I find it to be extremely reliable.
    Yeah, I was simply frustrated earlier. The product is great once you know how to use it, but there are a lot of little undocumented (or poorly documented) things that cause me to pull my hair out. For instance, I didn't know constants can't be passed to modules until Reese mentioned it. Maybe this is buried in the module help somewhere, but I've read it several times and didn't see anything.

    It's OK, though - we've got this forum, which makes a wonderful substitute. :) Very nice to be able to pop in and ask questions, and I greatly appreciate AMX making it available.
  • TCP communications howto

    I agree that some things are not documented as well as everyone would like and the Forums are a great way for us to get information out to people for specific needs. I was going to ask the AMX folks running the Forums if they could create for us a Tech Note and/or Tutorial forum so we could contribute to the growing AMX database of technical information that can not be found in a manual.

    We had a thread on TIMELINEs a few days ago where I learned (for the first time) that the IDs are module specific (this is not documented anywhere). It helped to explain why a technique I was using with REBUILD_EVENT() would not work and further I learned that I had gone to great lengths to code around something that was not even a problem! Much of the information came from an AMX engineer - we appreciate their participation in various forums. Forums are a great tool for learning and sharing information.

    Reese
Sign In or Register to comment.