AdaFruit IO meets my basic criteria as it has support for HTTP/S clients (it also has an MQTT interface which I will look at in a future post) and the API is well documented.
My first Proof of Concept (PoC) was to build a desktop client which used the HttpWebRequest (for ease of porting to NetMF) classes to upload data.
The program uploaded one of three simulated values to AdaFruit.IO every 10 seconds.
I found the username, group, and feed keys to be case sensitive so pay close attention to the values displayed in the webby UI or copy n paste.
program.cs
class Program { static void Main(string[] args) { string adaFruitIOApiBaseUrl = "https://IO.adafruit.com/api/v2/"; string adaFruitIOUserName = "YourUserName"; // This is mixed case & case sensitive string adaFruitIOApiKey = "YourAPIKey"; // The feed group and feed key are forced to lower case by UI const string feedGroup = ""; //const string feedGroup = "devduinov2-dot-2"; const string temperatureKey = "t"; const double temperatureBase = 20.0; const double temperatureRange = 10.0; const string humidityKey = "h"; const double humidityBase = 70.0; const double humidityRange = 20.0; const string batteryVoltageKey = "v"; const double batteryVoltageBase = 3.00; const double batteryVoltageRange = -1.00; TimeSpan dataUpdateDelay = new TimeSpan(0, 0, 10); Random random = new Random(); while (true) { double temperature = temperatureBase + random.NextDouble() * temperatureRange; Console.WriteLine("Temperature {0}°C", temperature.ToString("F1")); AdaFruitIoFeedUpdate(adaFruitIOApiBaseUrl, adaFruitIOUserName, adaFruitIOApiKey, feedGroup, temperatureKey, temperature.ToString("F1")); Thread.Sleep(dataUpdateDelay); double humidity = humidityBase + random.NextDouble() * humidityRange; Console.WriteLine("Humidity {0}%", humidity.ToString("F0")); AdaFruitIoFeedUpdate(adaFruitIOApiBaseUrl, adaFruitIOUserName, adaFruitIOApiKey, feedGroup, humidityKey, humidity.ToString("F0")); Thread.Sleep(dataUpdateDelay); double batteryVoltage = batteryVoltageBase + random.NextDouble() * batteryVoltageRange; Console.WriteLine("Battery voltage {0}V", batteryVoltage.ToString("F2")); AdaFruitIoFeedUpdate(adaFruitIOApiBaseUrl, adaFruitIOUserName, adaFruitIOApiKey, feedGroup, batteryVoltageKey, batteryVoltage.ToString("F2")); Thread.Sleep(dataUpdateDelay); } }
client.cs
public void AdaFruitIoFeedUpdate(string apiBaseUrl, string userName, string apiKey, string group, string feedKey, string value, int httpRequestTimeoutmSec = 2500, int httpRequestReadWriteTimeoutmSec = 5000) { string feedUrl; if (group.Trim() == string.Empty) { feedUrl = apiBaseUrl + userName + @"/feeds/" + feedKey + @"/data"; } else { feedUrl = apiBaseUrl + userName + @"/feeds/" + group.Trim() + "." + feedKey + @"/data"; } Console.WriteLine(" Feed URL :{0}", feedUrl); try { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(feedUrl); { string payload = @"{""value"": """ + value + @"""}"; byte[] buffer = Encoding.UTF8.GetBytes(payload); DateTime httpRequestedStartedAtUtc = DateTime.UtcNow; request.Method = "POST"; request.ContentLength = buffer.Length; request.ContentType = @"application/json"; request.Headers.Add("X-AIO-Key", apiKey); request.KeepAlive = false; request.Timeout = httpRequestTimeoutmSec; request.ReadWriteTimeout = httpRequestReadWriteTimeoutmSec; using (Stream stream = request.GetRequestStream()) { stream.Write(buffer, 0, buffer.Length); } using (var response = (HttpWebResponse)request.GetResponse()) { Console.WriteLine(" Status: " + response.StatusCode + " : " + response.StatusDescription); } TimeSpan duration = DateTime.UtcNow - httpRequestedStartedAtUtc; Console.WriteLine(" Duration: " + duration.ToString()); } } catch (Exception ex) { Console.WriteLine(ex.Message); throw; } } }
This approach seemed to work pretty reliably