/**
 * Keeps receiving packets and calls callback function
 */
void UDPmessenger::runLoop() {

	while(true) {	// Iterate until buffer is empty:
  	  uint16_t packetSize = _Udp.parsePacket();
	  if (_Udp.available() && packetSize !=0)   {  
		resetTXBuffer();
//  		Serial.print(F("Packet size: "));
//	    Serial.println(packetSize, DEC);

	    // Read packet header of 2 bytes (length):
	    _Udp.read(_rxPacketBuffer, UDPmessenger_PGKHDR_SIZE);

	    // Read out packet length (first word):
	    uint16_t packetLength = word(_rxPacketBuffer[0] & B00000111, _rxPacketBuffer[1]);

	    if (packetSize==packetLength) {  // Just to make sure these are equal, they should be!
			if (packetLength > UDPmessenger_PGKHDR_SIZE)	{
				_parsePacket(packetLength);
				send();	// Sends a response if we build one
			}
	    } else {
			if (_serialOutput) 	{
	      		Serial.print(F("ERROR: Packet size mismatch: "));
			    Serial.print(packetSize, DEC);
			    Serial.print(F(" != "));
			    Serial.println(packetLength, DEC);
			}
			// Flushing the buffer:
			// TODO: Other way? _Udp.flush() ??
	          while(_Udp.available()) {
	              _Udp.read(_rxPacketBuffer, UDPmessenger_BUFFER_SIZE);
	          }
	    }

  		//Serial.println(F("Done with packet"));
	  } else {
		break;	// Exit while(true) loop because there is no more packets in buffer.
	}
  }
}
/**
 * Keeps connection to the switcher alive - basically, this means answering back to ping packages.
 * Therefore: Call this in the Arduino loop() function and make sure it gets call at least 2 times a second
 * Other recommendations might come up in the future.
 */
void ATEM::runLoop() {

  // WARNING:
  // It can cause severe timing problems using "slow" functions such as Serial.print*() 
  // in the runloop, in particular during "boot" where the ATEM delivers some 10-20 kbytes of system status info which
  // must exit the RX-buffer quite fast. Therefore, using Serial.print for debugging in this 
  // critical phase will in it self affect program execution!

  // Limit of the RX buffer of the Ethernet interface is another general issue.
  // When ATEM sends the initial system status packets (10-20 kbytes), they are sent with a few microseconds in between
  // The RX buffer of the Ethernet interface on Arduino simply does not have the kapacity to take more than 2k at a time.
  // This means, that we only receive the first packet, the others seems to be discarded. Luckily most information we like to 
  // know about is in the first packet (and some in the second, the rest is probably thumbnails for the media player).
  // It may be possible to bump up this buffer to 4 or 8 k by simply re-configuring the amount of allowed sockets on the interface.
  // For some more information from a guy seemingly having a similar issue, look here:
  // http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1282170842

	uint16_t packetSize = 0;


	if (_isConnectingTime > 0)	{

			// Waiting for the ATEM to answer back with a packet 20 bytes long.
			// According to packet analysis with WireShark, this feedback from ATEM
			// comes within a few microseconds!
		packetSize = _Udp.parsePacket();
		if (_Udp.available() && packetSize==20)   {  	

				// Read the response packet. We will only subtract the session ID
				// According to packet analysis with WireShark, this feedback from ATEM
				// comes a few microseconds after our connect invitation above. Two packets immediately follow each other.
				// After approx. 200 milliseconds a third packet is sent from ATEM - a sort of re-sent because it gets impatient.
				// And it seems that THIS third packet is the one we actually read and respond to. In other words, I believe that 
				// the ethernet interface on Arduino actually misses the first two for some reason!
			_Udp.read(_packetBuffer,20);
			_sessionID = _packetBuffer[15];

			// Send connectAnswerString to ATEM:
			_Udp.beginPacket(_switcherIP,  9910);
	
			// TODO: Describe packet contents according to rev.eng. API
			byte connectHelloAnswerString[] = {  
			  0x80, 0x0c, 0x53, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00 };
			_Udp.write(connectHelloAnswerString,12);
			_Udp.endPacket();

			_isConnectingTime = 0;	// End connecting
		} else {
			if (_isConnectingTime+2000 < (unsigned long)millis())	{
				if (_serialOutput) 	{
		      		Serial.println(F("Timeout waiting for ATEM switcher response"));
				}
				_isConnectingTime = 0;
			}
		}
	} else {
	



	  // If there's data available, read a packet, empty up:
	 // Serial.println("ATEM runLoop():");
	  while(true) {	// Iterate until buffer is empty:
	  	  packetSize = _Udp.parsePacket();
		  if (_Udp.available() && packetSize !=0)   {  
		//	Serial.print("New Packet");
			//	Serial.print(("PACKET: "));
			  //  Serial.println(packetSize, DEC);

		    // Read packet header of 12 bytes:
		    _Udp.read(_packetBuffer, 12);

		    // Read out packet length (first word), remote packet ID number and "command":
		    uint16_t packetLength = word(_packetBuffer[0] & B00000111, _packetBuffer[1]);
		    _lastRemotePacketID = word(_packetBuffer[10],_packetBuffer[11]);
		    uint8_t command = _packetBuffer[0] & B11111000;
		    boolean command_ACK = command & B00001000 ? true : false;	// If true, ATEM expects an acknowledgement answer back!
		    boolean command_INIT = command & B00010000 ? true : false;	// If true, ATEM expects an acknowledgement answer back!
				// The five bits in "command" (from LSB to MSB):
				// 1 = ACK, "Please respond to this packet" (using the _lastRemotePacketID). Exception: The initial 10-20 kbytes of Switcher status
				// 2 = ?. Set during initialization? (first hand-shake packets contains that)
				// 3 = "This is a retransmission". You will see this bit set if the ATEM switcher did not get a timely response to a packet.
				// 4 = ? ("hello packet" according to "ratte", forum at atemuser.com)
				// 5 = "This is a response on your request". So set this when answering...


		    if (packetSize==packetLength) {  // Just to make sure these are equal, they should be!
			  _lastContact = millis();
		
		      // If a packet is 12 bytes long it indicates that all the initial information 
		      // has been delivered from the ATEM and we can begin to answer back on every request
			  // Currently we don't know any other way to decide if an answer should be sent back...
		      if(!_hasInitialized && packetSize == 12) {
		        _hasInitialized = true;
				if (_serialOutput) Serial.println(F("_hasInitialized=TRUE"));
		      } 
	
				if (packetLength > 12 && !command_INIT)	{	// !command_INIT is because there seems to be no commands in these packets and that will generate an error.
					_parsePacket(packetLength);
				}

		      // If we are initialized, lets answer back no matter what:
				// TODO: "_hasInitialized && " should be inserted back before "command_ACK" but 
				// with Arduino 1.0 UDP library it has proven MORE likely that the initial
				// connection is made if we ALWAYS answer the switcher back.
				// Apparently the initial "chaos" of keeping up with the incoming data confuses 
				// the UDP library so that we might never get initialized - and thus never get connected
				// So... for now this is how we do it:
				// CHANGED with arduino 1.0.1..... put back in.
		      if (_hasInitialized && command_ACK) {
		        if (_serialOutput) {
					Serial.print(F("ACK, rpID: "));
		        	Serial.println(_lastRemotePacketID, DEC);
				}

		        _sendAnswerPacket(_lastRemotePacketID);
		      }

		    } else {
				if (_serialOutput) 	{
		  /*    		Serial.print(("ERROR: Packet size mismatch: "));
				    Serial.print(packetSize, DEC);
				    Serial.print(" != ");
				    Serial.println(packetLength, DEC);
			*/	}
				// Flushing the buffer:
				// TODO: Other way? _Udp.flush() ??
		          while(_Udp.available()) {
		              _Udp.read(_packetBuffer, 96);
		          }
		    }
		  } else {
			break;	// Exit while(true) loop because there is no more packets in buffer.
		}
	  }
	}
}
Exemple #3
0
void MDNSResponder::update() {
  if (!_conn || !_conn->next()) {
    return;
  }
  _parsePacket();
}
void ATEMbase::runLoop(uint16_t delayTime) {
	
	static bool neverConnected = true;
	if (neverConnected)	{
		neverConnected = false;
		connect();
//		Serial.println("Connecting first time...");
	}

	unsigned long enterTime = millis();
	
	static boolean waitingForIncoming = false;

	do {
		while(true) {	// Iterate until UDP buffer is empty
			uint16_t packetSize = _Udp.parsePacket();
		
			if (_Udp.available())   {  	

				_Udp.read(_packetBuffer,12);	// Read header
				 _sessionID = word(_packetBuffer[2], _packetBuffer[3]);
				 uint8_t headerBitmask = _packetBuffer[0]>>3;
				 _lastRemotePacketID = word(_packetBuffer[10],_packetBuffer[11]);
			 	 if (_lastRemotePacketID < ATEM_maxInitPackageCount)	{
			 	 	_missedInitializationPackages[_lastRemotePacketID>>3] &= ~(B1<<(_lastRemotePacketID&0x07));
			 	 }

				 uint16_t packetLength = word(_packetBuffer[0] & B00000111, _packetBuffer[1]);

			    if (packetSize==packetLength) {  // Just to make sure these are equal, they should be!
					_lastContact = millis();
					waitingForIncoming = false;
	
					if (headerBitmask & ATEM_headerCmd_HelloPacket)	{	// Respond to "Hello" packages:
						_isConnected = true;
					
						// _packetBuffer[12]	The ATEM will return a "2" in this return package of same length. If the ATEM returns "3" it means "fully booked" (no more clients can connect) and a "4" seems to be a kind of reconnect (seen when you drop the connection and the ATEM desperately tries to figure out what happened...)
						// _packetBuffer[15]	This number seems to increment with about 3 each time a new client tries to connect to ATEM. It may be used to judge how many client connections has been made during the up-time of the switcher?
						
						_wipeCleanPacketBuffer();
						_createCommandHeader(ATEM_headerCmd_Ack, 12);
						_packetBuffer[9] = 0x03;	// This seems to be what the client should send upon first request. 
						_sendPacketBuffer(12);  
					}

					// If a packet is 12 bytes long it indicates that all the initial information 
					// has been delivered from the ATEM and we can begin to answer back on every request
					// Currently we don't know any other way to decide if an answer should be sent back...
					// The QT lib uses the "InCm" command to indicate this, but in the latest version of the firmware (2.14)
					// all the camera control information comes AFTER this command, so it's not a clear ending token anymore.
					// However, I'm not sure if I checked the _lastRemotePacketID of the packages with the additional camera control info - if it was a resend, 
					// "InCm" may still indicate the number of the last init-package and that's all I need to request the missing ones....
			
					// BTW: It has been observed on an old 10Mbit hub that packages could arrive in a different order than sent and this may 
					// mess things up a bit on the initialization. So it's recommended to has as direct routes as possible.
					if(!_initPayloadSent && packetSize == 12 && _lastRemotePacketID>1) {
						_initPayloadSent = true;
						_initPayloadSentAtPacketId = _lastRemotePacketID;
						#if ATEM_debug 
						if (_serialOutput & 0x80) {
							Serial.print(F("_initPayloadSent=TRUE @rpID "));
							Serial.println(_initPayloadSentAtPacketId);
							Serial.print(F("Session ID: "));
							Serial.println(_sessionID, DEC);
						}
						#endif
					} 

					if (_initPayloadSent && (headerBitmask & ATEM_headerCmd_AckRequest) && (_hasInitialized || !(headerBitmask & ATEM_headerCmd_Resend))) { 	// Respond to request for acknowledge	(and to resends also, whatever...  
						_wipeCleanPacketBuffer();
						_createCommandHeader(ATEM_headerCmd_Ack, 12, _lastRemotePacketID);
						_sendPacketBuffer(12); 
					
						#if ATEM_debug 
				        if (_serialOutput & 0x80) {
							Serial.print(F("rpID: "));
				        	Serial.print(_lastRemotePacketID, DEC);
							Serial.print(F(", Head: 0x"));
							Serial.print(headerBitmask, HEX);
							Serial.print(F(", Len: "));
				        	Serial.print(packetLength, DEC);
							Serial.print(F(" bytes"));

							Serial.println(F(" - ACK!"));
						} else 
						#endif
						if (_serialOutput>1)	{
							Serial.print(F("rpID: "));
				        	Serial.print(_lastRemotePacketID, DEC);
							Serial.println(F(" - ACK!"));
						} 
					} else if(_initPayloadSent && (headerBitmask & ATEM_headerCmd_RequestNextAfter) && _hasInitialized) {	// ATEM is requesting a previously sent package which must have dropped out of the order. We return an empty one so the ATEM doesnt' crash (which some models will, if it doesn't get an answer before another 63 commands gets sent from the controller.)
						uint8_t b1 = _packetBuffer[6];
						uint8_t b2 = _packetBuffer[7];
						_wipeCleanPacketBuffer();
						_createCommandHeader(ATEM_headerCmd_Ack, 12, 0);
						_packetBuffer[0] = ATEM_headerCmd_AckRequest << 3;	// Overruling this. A small trick because createCommandHeader shouldn't increment local package ID counter
						_packetBuffer[10] = b1;
						_packetBuffer[11] = b2;
						_sendPacketBuffer(12); 

						if (_serialOutput>1)	{
							Serial.print(F("ATEM asking to resend "));
				        	Serial.println((b1<<8)|b2, DEC);
						}
					} else {
						#if ATEM_debug 
				        if (_serialOutput & 0x80) {
							Serial.print(F("rpID: "));
				        	Serial.print(_lastRemotePacketID, DEC);
							Serial.print(F(", Head: 0x"));
							Serial.print(headerBitmask, HEX);
							Serial.print(F(", Len: "));
				        	Serial.print(packetLength, DEC);
							Serial.println(F(" bytes"));
						} else 
						#endif
						if (_serialOutput>1)	{
							Serial.print(F("rpID: "));
				        	Serial.println(_lastRemotePacketID, DEC);
						}
					}
				
					if (!(headerBitmask & ATEM_headerCmd_HelloPacket) && packetLength>12)	{
						_parsePacket(packetLength);
					}
			    } else {
					#if ATEM_debug
					if (_serialOutput & 0x80) 	{
			      		Serial.print(F("ERROR: Packet size mismatch: "));
					    Serial.print(packetSize, DEC);
					    Serial.print(F(" != "));
					    Serial.println(packetLength, DEC);
					}
					#endif
					// Flushing:
			        while(_Udp.available()) {
			        	_Udp.read(_packetBuffer, ATEM_packetBufferLength);
			        }
			    }
			} else break;
		}