A little while ago I wrote an app to listen to data coming from my Photon via TCP. This probably doesn’t have all the bells and whistles that it should have for safety, but it does work (you might check out some of the posts from @rickkas7 on the subject of TCP) .
Here is the Photon code used to test the connection. It sends the same data every 10 seconds. For ease of determining the end of the transmission, I’ve used a delimiter at the end of the message.
TCPServer server = TCPServer(80);
TCPClient client;
char data[] = {"1 This is some fairly long data, that repeats this sentence multiple times. 2 This is some fairly long data, that repeats this sentence multiple times. 3 This is some fairly long data, that repeats this sentence multiple times. 4 This is some fairly long data, that repeats this sentence multiple times. 5 This is some fairly long data, that repeats this sentence multiple times.6 This is some fairly long data, that repeats this sentence multiple times. 7 This is some fairly long data, that repeats this sentence multiple times. 8 This is some fairly long data, that repeats this sentence multiple times. And now we have the end$#$"};
Timer transmitTimer(10000, transmitTimerHandler);
SYSTEM_MODE(SEMI_AUTOMATIC); // no need to connect to the cloud since this app is for local data transmission
void setup() {
server.begin();
WiFi.connect();
while (!WiFi.ready()) {}
}
void loop() {
client = server.available();
if (client) {
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
client.println("HTTP/1.1 200 OK");
client.println("Connection: keep-alive");
client.println("Content-Type: text/event-stream");
client.println();
transmitTimer.start();
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
delay(100);
}
}
void transmitTimerHandler() {
client.print(data);
}
The iOS code consists of a class ParticleTCPListener with the following code,
import Foundation
protocol ParticleListenerDataDelegate {
func receivedData(success: String?, error: String?)
}
class ParticleTCPListener: NSObject, URLSessionDataDelegate {
let url: URL
let delimiter: String
let retryTime: Double = 3 // in seconds
var task: URLSessionDataTask?
let receivedData = NSMutableData()
var delegate: ParticleListenerDataDelegate?
init(url: String, delimiter: String) {
self.url = URL(string: url)!
self.delimiter = delimiter
super.init()
self.connect()
}
func connect() {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = TimeInterval(INT_MAX)
configuration.timeoutIntervalForResource = TimeInterval(INT_MAX)
configuration.httpAdditionalHeaders = ["Accept": "text/event-stream", "Cache-Control": "no-cache"]
let urlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue())
self.task = urlSession.dataTask(with: self.url)
self.task!.resume()
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
self.receivedData.append(data)
let resultString = NSString.init(data: self.receivedData as Data, encoding: String.Encoding.utf8.rawValue)!
if resultString.hasSuffix(self.delimiter) {
let s = resultString.replacingOccurrences(of: self.delimiter, with: "")
DispatchQueue.main.async() {
self.delegate?.receivedData(success: "\(NSDate()): \(s)\n", error: nil)
}
self.receivedData.setData(NSData() as Data)
}
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
print("didReceiveError")
if error == nil || error!._code != -999 {
let delayTime = DispatchTime.now() + self.retryTime
DispatchQueue.main.asyncAfter(deadline: delayTime) {
self.connect()
}
}
DispatchQueue.main.async {
self.delegate?.receivedData(success: nil, error: (error?.localizedDescription)!)
}
}
}
…and this minimal code in the view controller,
import UIKit
class ViewController: UIViewController, ParticleListenerDataDelegate {
var eventSource: ParticleTCPListener!
override func viewDidLoad() {
super.viewDidLoad()
eventSource = ParticleTCPListener(url: "http://10.0.1.13", delimiter: "$#$")
eventSource.delegate = self
}
func receivedData(success: String?, error: String?) {
guard success != nil else {
print(error!)
return
}
print(success!)
print("\n\n")
}
}