Anyone knows how to download the Tello sdk for iOS. I have visited the link https://www.ryzerobotics.com/tello, but it is not useful
There isn't an official SDK to download for the Tello. You can find the instructions at this link which provides details of sending UDP packets over a network connection to the drone. You can use CocoaAsyncSocket found on GitHub to help you create the connection to the socket and then pass the commands as ASCII data to the Tello.
Here is some sample code to get you going (which I wrote earlier today on my own website):
var socket = GCDAsyncUdpSocket()
let sendHost = "192.168.10.1"
let sendPort: UInt16 = 8889
let statePort: UInt16 = 8890
First, create the socket, a String with the Tello IP, and a couple of constants holding the port numbers (8889 is used to send commands to, and also receives responses back such as "OK" if the command was successful. 8890 is used for receiving telemetry from the drone multiple times per second.
This will send the initial "command" command to the Tello:
// Set the delegate and dispatch queue
socket.setDelegate(self)
socket.setDelegateQueue(DispatchQueue.main)
// Send the "command" command to the socket.
do {
try socket.bind(toPort: sendPort)
try socket.enableBroadcast(true)
try socket.beginReceiving()
socket.send("command".data(using: String.Encoding.utf8)!,
toHost: sendHost,
port: sendPort,
withTimeout: 0,
tag: 0)
} catch {
print("Command command sent.")
}
You need to make sure that the class conforms to GCDAsyncUdpSocketDelegate.
Implement the delegate method which receives data from the Tello.
func udpSocket(_ sock: GCDAsyncUdpSocket, didReceive data: Data, fromAddress address: Data, withFilterContext filterContext: Any?) {
let dataString = String(data: data, encoding: String.Encoding.utf8)
if (sock.localPort() == sendPort) {
print(dataString)
}
if (sock.localPort() == statePort) {
var telloStateDictionary = [String:Double]()
let stateArray = dataString?.components(separatedBy: ";")
for itemState in stateArray! {
let keyValueArray = itemState.components(separatedBy: ":")
if (keyValueArray.count == 2) {
telloStateDictionary[keyValueArray[0]] = Double(keyValueArray[1])
}
}
print(telloStateDictionary)
}
}
Set up port 8890 for listening to the telemetry:
let receiveSocket = GCDAsyncUdpSocket(delegate: self, delegateQueue: DispatchQueue.main)
do {
try receiveSocket.bind(toPort: statePort)
} catch {
print("Bind Problem")
}
do {
try receiveSocket.beginReceiving()
} catch {
print("Receiving Problem")
}
Run the app. Assuming you have manually connected to the Tello (like you would if you were controlling it with the Tello app), you should begin getting information back from the Tello.
To send more commands to it, such as "takeoff" or "cw 360", you can do something such as:
func sendCommand(command: String) {
let message = command.data(using: String.Encoding.utf8)
socket.send(message!, toHost: sendHost, port: sendPort, withTimeout: 2, tag: 0)
}
Pass in the command as a string, and this will send it to the Tello.
Related
I have an XCTest which works with UI components. I tried to open a server socket within the xctext function using SwiftNIO.
I took the echo server example from here. and I simplified, removed the args with hardcoded values for the sake of a dirty test.
import XCTest
import NIOCore
import NIOPosix
private final class EchoHandler: ChannelInboundHandler {
public typealias InboundIn = ByteBuffer
public typealias OutboundOut = ByteBuffer
public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
// As we are not really interested getting notified on success or failure we just pass nil as promise to
// reduce allocations.
context.write(data, promise: nil)
}
// Flush it out. This can make use of gathering writes if multiple buffers are pending
public func channelReadComplete(context: ChannelHandlerContext) {
context.flush()
}
public func errorCaught(context: ChannelHandlerContext, error: Error) {
print("error: ", error)
// As we are not really interested getting notified on success or failure we just pass nil as promise to
// reduce allocations.
context.close(promise: nil)
}
}
class MyXCTests: XCTestCase {
var app: XCUIApplication!
override func setUpWithError() throws {
continueAfterFailure = false
app = XCUIApplication()
// Catch system alerts such as "allow connecting to Wi-fi network"
addUIInterruptionMonitor(withDescription: "System Dialog") { (alert) -> Bool in
alert.buttons["Join"].tap()
return true
}
}
override func tearDownWithError() throws {
}
func testXYZ() throws {
app.launch()
let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
let bootstrap = ServerBootstrap(group: group)
// Specify backlog and enable SO_REUSEADDR for the server itself
.serverChannelOption(ChannelOptions.backlog, value: 256)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
// Set the handlers that are appled to the accepted Channels
.childChannelInitializer { channel in
// Ensure we don't read faster than we can write by adding the BackPressureHandler into the pipeline.
channel.pipeline.addHandler(BackPressureHandler()).flatMap { v in
channel.pipeline.addHandler(EchoHandler())
}
}
// Enable SO_REUSEADDR for the accepted Channels
.childChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.childChannelOption(ChannelOptions.maxMessagesPerRead, value: 16)
.childChannelOption(ChannelOptions.recvAllocator, value: AdaptiveRecvByteBufferAllocator())
defer {
try! group.syncShutdownGracefully()
}
let channel = try { () -> Channel in
return try bootstrap.bind(host: "0.0.0.0", port: 1234).wait()
}()
print("============= Server started and listening on \(channel.localAddress!)")
// then some XCTest code which works here was cut from this snippet
}
The test runs correctly, it also prints
============= Server started and listening on [IPv4]0.0.0.0/0.0.0.0:1234
But in reality EchoClient from here doesn't work
swift run NIOEchoClient localhost 1234 1785
[0/0] Build complete!
Please enter line to send to the server
dfsdfd
Swift/ErrorType.swift:200: Fatal error: Error raised at top level: NIOPosix.NIOConnectionError(host: "localhost", port: 1234, dnsAError: nil, dnsAAAAError: nil, connectionErrors: [NIOPosix.SingleConnectionFailure(target: [IPv6]localhost/::1:1234, error: connection reset (error set): Connection refused (errno: 61)), NIOPosix.SingleConnectionFailure(target: [IPv4]localhost/127.0.0.1:1234, error: connection reset (error set): Connection refused (errno: 61))])
[1] 28213 trace trap swift run NIOEchoClient localhost 1234
The listening socket also unavailable with
sudo lsof -PiTCP -sTCP:LISTEN
I was also trying UITestEntitlements to set com.apple.security.app-sandbox to false.
Is there a way to allow server sockets from XCTest?
Originally I was trying to embed a Swift-GRPC endpoint, to allow more finer grained control from a HW in the loop controller. The intent is to start an XCTest using command line xcodebuild, which in turn is starting a long running test, but instead of the test code written in Swift, I would expose the events when to tap some buttons, right outside of the test process through a grpc endpoint.
Since the grpc endpoint didn't worked, I reduced the problem to the one above.
Anybody have a hint, how to pass through this issue, or have a hint why it will never be possible to open server socket within an XCTest app, don't hesitate to reply here.
Yes, that is possible, you can find many examples of this in the AsyncHTTPClient and SwiftNIO test suites.
The reason that yours doesn't work is because you shut down the MultiThreadedEventLoopGroup right after binding the socket. So essentially you're starting everything up and then you shut it down again.
Also, for unit tests, I'd recommend binding to 127.0.0.1 only because you probably don't want connections from elsewhere. Another good idea is to use an ephemeral port, ie. have the system pick a free, random port automatically. You can achieve this by specifying port 0. After you bind the server Channel you can then interrogate the server channel by using serverChannel.localAddress?.port! about the port it picked.
Here's a full example with a client and a server in a test case.
import XCTest
import NIO
final class ExampleTests: XCTestCase {
// We keep the `EventLoopGroup` where all the I/O runs alive during the test
private var group: EventLoopGroup!
// Same for the server channel.
private var serverChannel: Channel!
private var serverAddress: SocketAddress {
return self.serverChannel.localAddress!
}
// We set up the server in `setUp`...
override func setUp() {
self.group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
XCTAssertNoThrow(self.serverChannel = try ServerBootstrap(group: self.group)
.childChannelInitializer { channel in
channel.pipeline.addHandler(UppercasingHandler())
}
.bind(host: "127.0.0.1", port: 0) // port 0 means: pick a free port.
.wait())
}
// ... and tear it down in `tearDown`.
override func tearDown() {
XCTAssertNoThrow(try self.serverChannel?.close().wait())
XCTAssertNoThrow(try self.group?.syncShutdownGracefully())
}
func testExample() throws {
// Here we just bootstrap a little client that sends "Hello world!\n"
let clientChannel = try ClientBootstrap(group: self.group)
.channelInitializer { channel in
channel.pipeline.addHandler(PrintEverythingHandler())
}
.connect(to: self.serverAddress)
.wait()
XCTAssertNoThrow(try clientChannel
.writeAndFlush(ByteBuffer(string: "Hello world!\n"))
.wait())
XCTAssertNoThrow(try clientChannel.closeFuture.wait())
}
}
// Just a handler that uses the C `toupper` function which uppercases characters.
final class UppercasingHandler: ChannelInboundHandler {
typealias InboundIn = ByteBuffer
typealias OutboundOut = ByteBuffer
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
let inBuffer = self.unwrapInboundIn(data)
var outBuffer = context.channel.allocator.buffer(capacity: inBuffer.readableBytes)
// Here we just upper case each byte using the C stdlib's `toupper` function.
outBuffer.writeBytes(inBuffer.readableBytesView.map { UInt8(toupper(CInt($0))) })
context.writeAndFlush(self.wrapOutboundOut(outBuffer),
promise: nil)
}
// We want to close the connection on any error really.
func errorCaught(context: ChannelHandlerContext, error: Error) {
print("server: unexpected error \(error), closing")
context.close(promise: nil)
}
}
// This handler just prints everything using the `write` system call. And closes the connection on a newline.
final class PrintEverythingHandler: ChannelInboundHandler {
typealias InboundIn = ByteBuffer
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
let inBuffer = self.unwrapInboundIn(data)
guard inBuffer.readableBytes > 0 else {
return
}
// We're using Unsafe* stuff here because we're using the `write` system call, which is a C function.
_ = inBuffer.withUnsafeReadableBytes { ptr in
write(STDOUT_FILENO, ptr.baseAddress!, ptr.count)
}
// If we see a newline, then let's actually close the connection...
if inBuffer.readableBytesView.contains(UInt8(ascii: "\n")) {
print("found newline, closing...")
context.close(promise: nil)
}
}
func errorCaught(context: ChannelHandlerContext, error: Error) {
print("client: unexpected error \(error), closing")
context.close(promise: nil)
}
}
I am writing an application which sends commands and receives data via UDP network connection with a device.
I am trying to create an object which handles all the network related tasks.
I want the object to make a connection, send a command string to the connection, and receive data from the connection. I can get all the pieces to work but they happen out of sync with the running program. It is hard to explain but let me show you the code first then explain the issue.
import Foundation
import Network
var myConnection: NWConnection?
var backToString = "test"
class NetworkUDP: NSObject{
func makeConnection(){
let myPort = NWEndpoint.Port(rawValue: 50536)
let myHost = NWEndpoint.Host("192.168.7.239")
myConnection = NWConnection(host: myHost, port: myPort!, using: .udp)
myConnection?.start(queue: .main)
}
func send(myCommand: String) {
myConnection?.send(content: myCommand.data(using: String.Encoding.utf8), completion: NWConnection.SendCompletion.contentProcessed({(NWError) in print(NWError as Any)}))
print(myCommand)
}
func receive() {
myConnection?.receiveMessage { (data, context, isComplete, error) in
if (data != nil) {
backToString = String(decoding: data!, as : UTF8.self)
print(backToString)
} else {
print("Data = nil")
}
}
}
}
So if I instantiate this object and issue makeConnection(), send(myCommand: aCommand), receive() statements from a ViewController everything works but the functions run and return before the commands are actually sent and values returned.
In other words, I can't return the value of backToString as a return value from the function receive(). If I do it will always return "test" which is the initial value. It will eventually be replaced with the string value returned from the device but only after a delay and only after the function has already returned.
What I want to do is make a receive function of the form receive() -> String where String is the text string returned from the device that was sent a command. But I guess this isn't possible because the receive function returns before backToString actually receives any data. I am a bit confused by this. It seems like the function should stay halted until a value is received but it doesn't it just returns before the backToString variable receives the data from the UDP connection. However the print statement in the receive function does print the correct data but it only does so after the function receive has already returned.
I'm trying GRPC-Swift for Client-Server application.
I'm using GRPC-Swift for both Client and Server
Client is an iPhone application, which I tried with iPhone Simulator.
I followed this link for Client-side streaming RPC.
When I send message to Server from Client, I got the following error message in the console from Server,
error io.grpc.server_channel_call : unable to determine http version
From the Server in the
HTTPProtocolSwitcher.swift
inside the function func channelRead(context: ChannelHandlerContext, data: NIOAny), it is checking for HTTPProtocolVersion, and it is missing.
How to send the HTTPVersion from the Client code?
Update:
Client Code
import GRPC
import NIO
class HTTPClient {
private let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
private var channel: ClientConnection?
private var client: ChatGuide_ChatGuideClient?
private var clientCall: ClientStreamingCall<ChatGuide_TextMessage, ChatGuide_TextMessage>?
func connect(host: String, port: Int) throws {
let channel = ClientConnection.secure(group: self.group)
.connect(host: host, port: port)
self.channel = channel
self.client = ChatGuide_ChatGuideClient(channel: channel)
}
func disconnect() {
do {
self.clientCall?.sendEnd(promise: nil)
_ = try self.clientCall?.status.wait()
try self.group.syncShutdownGracefully()
} catch let error {
print("\(type(of: self)): Could not shutdown gracefully -", error.localizedDescription)
}
}
func initiateClient() {
let timeAmount = TimeAmount.minutes(1)
let timeLimit = TimeLimit.timeout(timeAmount)
let options = CallOptions(timeLimit: timeLimit)
let call = self.client?.chat(callOptions: options)
call?.response.whenSuccess { (message) in
print("\(type(of: self)): Message from server -", message.text)
}
call?.response.whenFailure { (error) in
print("\(type(of: self)): Response error -", error.localizedDescription)
}
self.clientCall = call
}
func send(text: String) {
if self.clientCall == nil {
self.initiateClient()
}
let message = ChatGuide_TextMessage.with {
$0.text = text
}
self.clientCall?.sendMessage(message, promise: nil)
}
}
Hey Vignesh,
I am currently learning gRPC-Swift myself, so I hope I will be of service and not muck things further.
However, it looks to me that you are not configuring the HTTP/1.x layer in order to transfer Protobuf packets, if you take a look at the HTTP1ToGRPCServerCodec.swift file Here
I think you will have a much clearer idea of how to adjust your code, I am sorry I can't provide more details, however not being too sure myself without further testing and reviewing the codebase.
Best regards and keep me posted if indeed i was helpful,
cheers
From the Server I have initiated insecure Server as,
let server = Server.insecure(group: self.group)
From the Client I have initiated secure ClientConnection as,
let channel = ClientConnection.secure(group: self.group)
And I got this clarification from here
So I made the ClientConnection also insecure as,
let channel = ClientConnection.insecure(group: self.group)
And after this it is working now.
I'm trying to build a TCP server with SwiftNIO. The server starts in the net, but the clients don't know the ip address. Therefore I want to start an UDP server as well and if the clients comes up, he sends a broadcast message to the net. The server will receive and answer, so that the client now knows the IP address.
Is it possible to build something like this with SwiftNIO?
Yes, that's possible also there's not much support in SwiftNIO to make this easy.
See below for a commented example which will send HELLO WORLD once a second to en0's broadcast address and port 37020.
import NIO
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
defer {
try! group.syncShutdownGracefully()
}
let matchingInterfaces = try System.enumerateInterfaces().filter {
// find an IPv4 interface named en0 that has a broadcast address.
$0.name == "en0" && $0.broadcastAddress != nil
}
guard let en0Interface = matchingInterfaces.first, let broadcastAddress = en0Interface.broadcastAddress else {
print("ERROR: No suitable interface found. en0 matches \(matchingInterfaces)")
exit(1)
}
// let's bind the server socket
let server = try! DatagramBootstrap(group: group)
// enable broadast
.channelOption(ChannelOptions.socket(SOL_SOCKET, SO_BROADCAST), value: 1)
.bind(to: en0Interface.address)
.wait()
print("bound to \(server.localAddress!)")
var buffer = server.allocator.buffer(capacity: 32)
buffer.writeString("HELLO WORLD!")
var destAddr = broadcastAddress
destAddr.port = 37020 // we're sending to port 37020
// now let's just send the buffer once a second.
group.next().scheduleRepeatedTask(initialDelay: .seconds(1),
delay: .seconds(1),
notifying: nil) { task in
server.writeAndFlush(AddressedEnvelope(remoteAddress: destAddr,data: buffer)).map {
print("message sent to \(destAddr)")
}.whenFailure { error in
print("ERROR: \(error)")
// and stop if there's an error.
task.cancel()
server.close(promise: nil)
}
}
try server.closeFuture.wait()
In case you want to bind to 0.0.0.0 and send to 255.255.255.255 you can use this
import NIO
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
defer {
try! group.syncShutdownGracefully()
}
// let's bind the server socket
let server = try! DatagramBootstrap(group: group)
// enable broadast
.channelOption(ChannelOptions.socket(SOL_SOCKET, SO_BROADCAST), value: 1)
.bind(to: .init(ipAddress: "0.0.0.0", port: 0))
.wait()
print("bound to \(server.localAddress!)")
var buffer = server.allocator.buffer(capacity: 32)
buffer.writeString("HELLO WORLD!")
// we're sending to port 37020
let destPort = 37020
let destAddress = try SocketAddress(ipAddress: "255.255.255.255", port: destPort)
// now let's just send the buffer once a second.
group.next().scheduleRepeatedTask(initialDelay: .seconds(1),
delay: .seconds(1),
notifying: nil) { task in
server.writeAndFlush(AddressedEnvelope(remoteAddress: destAddress, data: buffer)).map {
print("message sent to \(destAddress)")
}.whenFailure { error in
print("ERROR: \(error)")
// and stop if there's an error.
task.cancel()
server.close(promise: nil)
}
}
try server.closeFuture.wait()
I am trying to implement swift socket in my application. I am pretty new to socket programming. I was able to send messages through the socket. But I am unable to listen to any messages from server. I have implemented the following code:
let host = Urls.GetServerIP()
let port = portNo
client = TCPClient(address: host, port: Int32(port))
switch client?.connect(timeout: 10) {
case .success?:
print((readResponse(from: client!)))
case .failure(let error)?:
print( String(describing: error))
break
case .none: break
}
In the above code, I am able to read an incoming message which comes as soon as I connect. But, I am unable to find a way to know that there is a message from the server, after couple of minutes.
Are there any specific call backs to know when a new message arrives from the server?
I have implemented StreamDelegate to my class. But no call backs are being triggered.
Thanks
It seems that your code lacks continuous reading, which is the reason that you only get a message once. I used to use SwiftSocket myself some time ago. The problem with this framework is that it is too simple and lacks a lot of functionality, for example encryption.
I switched to CocoaAsyncSocket, which is the Swiss army knife to handle socket connections in Swift.
Here is a short Example:
import CocoaAsyncSocket
class TcpSocketConnection: GCDAsyncSocketDelegate {
let tcpSocket: GCDAsyncSocket?
init(host: String, port: UInt16) {
self.tcpSocket = GCDAsyncSocket(delegate: self)
do {
try tcpSocket?.connect(toHost: host, onPort: port, withTimeout: 5.0)
} catch let error {
print("Cannot open socket to \(host):\(port): \(error)")
self.tcpSocket = nil
}
}
func socket(_ sock: GCDAsyncSocket, didConnectToHost host: String, port: UInt16) {
self.tcpSocket?.readData(toLength: 1024, withTimeout: 60.0)
}
func socket(_ sock: GCDAsyncSocket, didRead data: Data, withTag tag: Int) {
// Process data
self.tcpSocket?.readData(toLength: 1024, withTimeout: 60.0, tag: 0)
}
}
The code highly depends from how your server behaves and what you exactly want to do. Socket handling is not an easy task, especially when you add encryption (which you should do in modern applications). You can use the documentation as a starting point but you will have to read and learn a lot.
You can use SocketIOClient for web sockets. It can be used to send or receive messages through sockets.
Link:
https://github.com/nuclearace/Socket.IO-Client-Swift