This is my first time writing Swift and using Xcode, so please bear with me.
I'm trying to write an iPhone app which takes values from the device's gyroscope/magnetometer and sends them in UDP packets to a specific IP address and port, at a specific rate (1-100Hz). This way, I can strap my phone to my head and use it as 'free' TrackIR alternative.
Opentrack , the software I'm using to receive the UDP packets, expects 48 bytes. 6 8-byte doubles in this order: X, Y, Z, Yaw, Pitch, Roll. Since I can't measure the phone's position relative to the display, the first 3 doubles will be 0. While Yaw, Pitch and Roll will be the phone's attitude in degrees.
Here's a stripped down version of my code so far ( full file here ):
import UIKit
import CoreMotion
import Network
class ViewController: UIViewController {
var enabled = false
var rate: Int = 100
var motion = CMMotionManager()
var connection: NWConnection?
@IBOutlet weak var activeState: UILabel!
@IBOutlet weak var ipAddress: UITextField!
@IBOutlet weak var port: UITextField!
@IBOutlet weak var rateDisplay: UILabel!
@IBOutlet weak var rateSlider: UISlider!
func radToDegData(value: Double) -> Data {
return withUnsafeBytes(of: value*180/Double.pi) { Data($0) }
}
func startStream(hostUDP: NWEndpoint.Host, portUDP: NWEndpoint.Port) {
motion.deviceMotionUpdateInterval = 1 / Double(rate)
motion.startDeviceMotionUpdates(to: OperationQueue.current!){ (data, error) in
if let trueData = data{
let UDPmessage = self.radToDegData(value: 0) + self.radToDegData(value: 0) + self.radToDegData(value: 0) + self.radToDegData(value: trueData.attitude.yaw) + self.radToDegData(value: trueData.attitude.pitch) + self.radToDegData(value: trueData.attitude.roll)
self.connection = NWConnection(host: hostUDP, port: portUDP, using: .udp)
self.sendUDP(UDPmessage)
self.connection?.start(queue: .global())
}
}
}
func stopStream() {
motion.stopDeviceMotionUpdates()
}
func sendUDP(_ content: Data) {
self.connection?.send(content: content, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
if (NWError == nil) {
//print("Data was sent to UDP")
} else {
//print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
}
})))
}
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func enableSwitch(_ sender: Any) {
enabled.toggle()
if enabled {
startStream(hostUDP: .init(ipAddress.text!), portUDP: NWEndpoint.Port(rawValue: UInt16(port.text ?? "4242")!) ?? NWEndpoint.Port.any)
} else {
stopStream()
}
}
}
I'm having 2 problems:
2020-12-13 22:03:44.765054+0100 UDPHeadTrack[5571:3061403] [] nw_path_evaluator_create_flow_inner NECP_CLIENT_ACTION_ADD_FLOW 31FA4B2D-1E8A-4D16-A1A6-D023471B59C0 [28: No space left on device]
2020-12-13 22:03:44.765089+0100 UDPHeadTrack[5571:3061403] [connection] nw_endpoint_flow_setup_channel [C513 192.168.0.1:4242 in_progress channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, ipv6, dns)] failed to request add nexus flow
Since this is my first Swift project, a good chunk of it is copy-pasted examples/documentation, which I'm assuming is why I'm experiencing these problems. Any help would be appreciated.
You are creating a new NWConnection
each time you receive a motion update. Your app can only have a limited number of open network connections (512 in this case). Once the quota is exceeded you get an error message.
A simple restructure is just to use an existing NWConnection
if it exists:
func startStream(hostUDP: NWEndpoint.Host, portUDP: NWEndpoint.Port) {
motion.deviceMotionUpdateInterval = 1 / Double(rate)
motion.startDeviceMotionUpdates(to: OperationQueue.current!){ (data, error) in
guard let trueData = data else {
return
}
if self.connection == nil {
self.connection = NWConnection(host: hostUDP, port: portUDP, using: .udp)
self.connection?.start(queue: .global())
}
let udpMessage = self.radToDegData(value: 0) + self.radToDegData(value: 0) + self.radToDegData(value: 0) + self.radToDegData(value: trueData.attitude.yaw) + self.radToDegData(value: trueData.attitude.pitch) + self.radToDegData(value: trueData.attitude.roll)
self.sendUDP(udpMessage)
}
}
func stopStream() {
motion.stopDeviceMotionUpdates()
self.connection?.cancel()
self.connection = nil
}
func sendUDP(_ content: Data) {
guard let connection = self.connection else {
return
}
connection.send(content: content, completion: NWConnection.SendCompletion.contentProcessed(({ (error) in
if let error = error {
print("ERROR! Error when data (Type: Data) sending. NWError: \n \(error)")
} else {
print("Data was sent to UDP")
}
})))
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.