Multicast is not supported by the Arduino Ethernet library but is supported by the underlying W5100 module. So to start off, this patch should be applied to the library (usually located in the arduino IDE directory under libraries\Ethernet\src).
Note that you may want to update the IP ranges below, I have used the 192.168.100.0/24 network for my tests and 239.100.100.100 multicast group.
Here is a modified version of the UDPSendReceive example:
And here is a corresponding "server" written in java (MLTestServer.java):
#compile
javac MLTestServer.java
#run
java -cp . MLTestServer
assuming off course javac and java is in the PATH.
Note that you may want to update the IP ranges below, I have used the 192.168.100.0/24 network for my tests and 239.100.100.100 multicast group.
Here is a modified version of the UDPSendReceive example:
/* UDPSendReceive.pde: This sketch receives Multicast UDP message strings, prints them to the serial port and sends an "acknowledge" string back to the group. created 21 Aug 2010 by Michael Margolis modified 09 Mar 2015 by Panos Gkikakis This code is in the public domain. */ #include <SPI.h> // needed for Arduino versions later than 0018 #include <Ethernet.h> #include <EthernetUdp.h> // UDP library from: [email protected] 12/30/2008 // Enter a MAC address and IP address for your controller below. // The IP address will be dependent on your local network: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // Arduino's IP IPAddress ip(192, 168, 100, 177); // Multicast group IP IPAddress mip(239, 100, 100, 100); // Port unsigned int localPort = 9999; // local port to listen on // buffers for receiving and sending data char packetBuffer[50]; //buffer to hold incoming packet, char ReplyBuffer[] = "acknowledged"; // a string to send back char hiBuffer[] = "0:Client ready"; // a string to send back int c = 0; // An EthernetUDP instance to let us send and receive packets over UDP EthernetUDP Udp; void setup() { // start the Ethernet and UDP: Ethernet.begin(mac, ip); Udp.beginMulti(mip, localPort); Serial.begin(9600); } void loop() { // if there's data available, read a packet int packetSize = Udp.parsePacket(); int n = 0; if (packetSize) { Serial.print("Received packet of size "); Serial.println(packetSize); Serial.print("From "); IPAddress remote = Udp.remoteIP(); for (int i = 0; i < 4; i++) { Serial.print(remote[i], DEC); if (i < 3) { Serial.print("."); } } Serial.print(", port "); Serial.println(Udp.remotePort()); // read the packet into packetBufffer n = Udp.read(packetBuffer, 50); packetBuffer[n] = 0; Serial.println("Contents:"); Serial.println(packetBuffer); // send a reply, to the IP address and port that sent us the packet we received Udp.beginPacket(mip, Udp.remotePort()); Udp.write(ReplyBuffer, sizeof(ReplyBuffer)); Udp.endPacket(); } if( c == 0 ) { hiBuffer[0] = (hiBuffer[0] > 56) ? 48 : (hiBuffer[0] + 1); Udp.beginPacket(mip, localPort); Udp.write(hiBuffer, sizeof(hiBuffer)); Udp.endPacket(); Serial.println(hiBuffer); c++; } else if( c > 600) { c = -1; } c++; delay(10); }
And here is a corresponding "server" written in java (MLTestServer.java):
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.MulticastSocket; import java.net.InetAddress; import java.net.DatagramPacket; import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.net.SocketAddress; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.concurrent.atomic.AtomicReference; public class MLTestServer { public static void main(String[] args) { String msg = "Server online. Keep 'em coming!\0"; String myIP = "192.168.100.1"; String multiAddress = "239.100.100.100"; int port = 9999; byte[] buf = new byte[100]; ConsoleReader cr = new ConsoleReader(); String ack = "acknowledged"; try { new Thread(cr).start(); InetAddress group = InetAddress.getByName(multiAddress); // Q: Why use two sockets one for sending and one for receiving? // A: There are issues when sending and receiving, namely some messages were not received. If you know why or if that's expected behavior pls let me know. // Q: Why use the long version of MulticastSocket constructor and joinGroup? // A: We need to specify the Network interface to get reliable results on multihomed hosts. MulticastSocket s = new MulticastSocket(new InetSocketAddress(InetAddress.getByName(myIP),port)); MulticastSocket r = new MulticastSocket(new InetSocketAddress(InetAddress.getByName(myIP),port)); s.joinGroup(new InetSocketAddress(group,port), NetworkInterface.getByInetAddress(InetAddress.getByName(myIP))); r.joinGroup(new InetSocketAddress(group,port), NetworkInterface.getByInetAddress(InetAddress.getByName(myIP))); // say hello DatagramPacket hi = new DatagramPacket(msg.getBytes(), msg.length(),group, port); s.send(hi); System.out.println("Started server: IP " + s.getLocalAddress() + " port " + s.getLocalPort()); System.out.println("\tMulticast: " + group.getAddress() + " port " + port); DatagramPacket recv = new DatagramPacket(buf, buf.length); // server loop r.setSoTimeout(5); while(!ConsoleReader.exit) { try { java.util.Arrays.fill(buf, (byte) 0); r.receive(recv); } catch(SocketTimeoutException te) { msg = cr.send.get(); if(msg != null && (msg.length() > 0)) { System.out.println("Sending: " + '"' + msg + '"'); hi = new DatagramPacket(msg.getBytes(), msg.length(),group, port); cr.send.set(""); s.send(hi); } continue; } msg = new String(recv.getData()); System.out.println("Message from: " + recv.getAddress() + ":" + recv.getPort() + ":\n" + msg); } s.leaveGroup(group); r.leaveGroup(group); s.close(); r.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } // thread to read console input class ConsoleReader implements Runnable { public static boolean exit = false; public AtomicReference<String> send = new AtomicReference<String>(); public void run() { String cmd; BufferedReader console = new BufferedReader(new InputStreamReader(System.in)); System.out.println("Commands:\t\nsend msg: sends a message (msg) to clients\t\nexit : end program"); while(true) { try { cmd = console.readLine(); if(cmd.compareToIgnoreCase("exit") == 0) { break; } else if (cmd.toLowerCase().startsWith("send ")) { this.send.set(cmd.substring(5) + "\0"); } else { System.out.println("Unknown command"); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); break; } } ConsoleReader.exit = true; } }The code should be self explanatory, I have included comments for some gotchas. To compile the java code simply run:
#compile
javac MLTestServer.java
#run
java -cp . MLTestServer
assuming off course javac and java is in the PATH.
No comments:
Post a Comment