sobota, 1 lutego 2020

C# - ThingSpeak

W tym poście chciałbym opisać krótką bibliotekę pozwalająca na zapis bądź odczyt danych z serwisu thingspeak.


[Źródło: https://docs.microsoft.com/en-us/dotnet/]

Biblioteka:

Dane z serwisu można pobierać poprzez przesłanie odpowiedniego żądania ze strony internetowej.  Odczytane dane przesłane są w formacie JSON.

W celu wprowadzenia danych należy wysłać następujące żądanie:

  1. https://api.thingspeak.com/update?api_key=<WRITE_KEY>&field1=<VALUE_TO_WRITE>

Jako odpowiedź przesłana będzie liczba oznaczająca numer aktualizacji danych.

Gdy chcemy odczytać dane to można użyć jednej z dwóch opcji. 

Odczyt zdefiniowanej liczby przesłanych danych (Read Channel Feed):

  1. https://api.thingspeak.com/channels/<CHANNEL_ID>/feeds.json?api_key=<READ_KEY>&results=<NUMBER_OF_DATA>

Otrzymane dane w formacie JSON wyglądają następująco:

  1. {
  2.     "channel":
  3.     {
  4.         "id":807800,
  5.         "name":"ESP32_DHT",
  6.         "latitude":"0.0",
  7.         "longitude":"0.0",
  8.         "field1":"Temp_DHT",
  9.         "field2":"Humi_DHT",
  10.         "created_at":"2019-06-23T15:03:48Z",
  11.         "updated_at":"2019-06-23T15:04:10Z",
  12.         "last_entry_id":3484
  13.     },
  14.     "feeds":[
  15.         {
  16.             "created_at":"2020-01-31T19:49:41Z",
  17.             "entry_id":3483,
  18.             "field1":"0",
  19.             "field2":null
  20.         },
  21.         {
  22.             "created_at":"2020-01-31T20:04:02Z",
  23.             "entry_id":3484,
  24.             "field1":"0",
  25.             "field2":null
  26.         }
  27.     ]
  28. }

Odczyt zdefiniowanej liczby danych z danego pola (Read Channel Field):

  1. https://api.thingspeak.com/channels/<CHANNEL_ID>/fields/<FIELD_ID>.json?api_key=<READ_KEY>&results=<NUMBER_OF_DATA_TO_READ>

Odczytane dane wyglądają następująco:

  1. {
  2.     "channel:
  3.     {
  4.         "id":807800,
  5.         "name":"ESP32_DHT",
  6.         "latitude":"0.0",
  7.         "longitude":"0.0",
  8.         "field1":"Temp_DHT",
  9.         "field2":"Humi_DHT",
  10.         "created_at":"2019-06-23T15:03:48Z",
  11.         "updated_at":"2019-06-23T15:04:10Z",
  12.         "last_entry_id":3484
  13. },
  14.       "feeds":
  15.         [
  16.             {
  17.                 "created_at":"2020-01-31T19:49:41Z",
  18.                 "entry_id":3483,
  19.                 "field1":"0"
  20.             },
  21.             {
  22.                 "created_at":"2020-01-31T20:04:02Z",
  23.                 "entry_id":3484,
  24.                 "field1":"0"
  25.             }
  26.         ]
  27.      }
  28. }

Ostatnia opcja pozwala na przeglądnięcie statusu aktualizacji danych. 

  1. https://api.thingspeak.com/channels/<CHANNEL_ID>/status.json?api_key=<READ_KEY>

Odebrany JSON dla powyższego zapytania wygląda następująco:

  1. {
  2.     "channel":
  3.     {
  4.         "name":"ESP32_DHT",
  5.         "latitude":"0.0",
  6.         "longitude":"0.0"
  7.     },
  8.     "feeds":
  9.     [
  10.         {
  11.             "created_at":"2020-01-31T19:46:13Z",
  12.             "entry_id":3480,"status":null
  13.         },
  14.         {
  15.             "created_at":"2020-01-31T19:48:21Z",
  16.             "entry_id":3481,"status":null
  17.         },
  18.         {
  19.             "created_at":"2020-01-31T19:49:02Z",
  20.             "entry_id":3482,
  21.             "status":null
  22.         },
  23.         {
  24.             "created_at":"2020-01-31T19:49:41Z",
  25.             "entry_id":3483,
  26.             "status":null
  27.         },
  28.         {
  29.             "created_at":"2020-01-31T20:04:02Z",
  30.             "entry_id":3484,
  31.             "status":null
  32.         }
  33.     ]
  34. }

Aby wysłać i odebrać dane należy posłużyć się następującymi funkcjami:

Pierwsze cztery funkcje przygotowują string z żądaniem, który należy podać jako parametr funkcji ostatniej gdzie wysyłana jest informacja do serwera a po niej następuje odebranie danych w formacie JSON. Funkcja zwraca cały odebrany obiekt.

  1. public string GetUpdateDataString(byte fieldNumber, int valueToWrite)
  2. {
  3.   return strUpdateBase + " ? key=" + _writeKey + "&field" + fieldNumber.ToString() + "=";
  4. }

  5. public string GetReadFeedString(byte numberOfDataToRead)
  6. {
  7.     return strReadData + _channelId +
  8.            strReadFeed_SecondPart + _readKey + "&results" + "=" + numberOfDataToRead;
  9. }

  10. public string GetReadFieldString(byte fieldNumber, byte numberOfDataToRead)
  11. {
  12.     return strReadData + _channelId +
  13.         strFields + fieldNumber.ToString() + strFields_2 + _readKey + "&results" + "=" + numberOfDataToRead;
  14. }

  15. public string GetReaddUpdateHistoryString(string channelID)
  16. {
  17.     return strReadData + _channelId + "/status.json?api_key=" + _readKey;
  18. }

  19. public string SendRequest(string requestToSend)
  20. {
  21.     string readedData = "";

  22.     try
  23.     {
  24.         HttpWebRequest ThingsSpeakReq;
  25.         HttpWebResponse ThingsSpeakResp;

  26.         ThingsSpeakReq = (HttpWebRequest)WebRequest.Create(requestToSend);
  27.         ThingsSpeakReq.Method = WebRequestMethods.Http.Get;
  28.         ThingsSpeakReq.Accept = "application/json";
  29.         ThingsSpeakResp = (HttpWebResponse)ThingsSpeakReq.GetResponse();

  30.         using (Stream responseStream = ThingsSpeakResp.GetResponseStream())
  31.         {
  32.             StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
  33.             readedData = (reader.ReadToEnd());
  34.         }

  35.         if (!(string.Equals(ThingsSpeakResp.StatusDescription"OK")))
  36.         {
  37.             Exception exData = new Exception(ThingsSpeakResp.StatusDescription);
  38.             return exData.ToString();
  39.         }
  40.     }
  41.     catch (Exception ex)
  42.     {
  43.         return ex.ToString();
  44.     }

  45.     return readedData;
  46. }

Odebrane dane w formacie JSON można obrobić za pomocą biblioteki np. Newtonsoft.Json.

Cała klasa wygląda następująco:

  1. class ThingspeakLib
  2. {
  3.     string _writeKey = "";
  4.     string _readKey = "";
  5.     string _channelId = "";
  6.     readonly string strUpdateBase = "http://api.thingspeak.com/update";
  7.     readonly string strReadData = "https://api.thingspeak.com/channels/";
  8.     readonly string strReadFeed_SecondPart = @"/feeds.json?api_key=";
  9.     readonly string strFields = @"/fields/";
  10.     readonly string strFields_2 = ".json?api_key=";
  11.     readonly string strStatusUpdates = @"/status.json?api_key=";
  12.     public string WriteKey
  13.     {
  14.         get { return _writeKey;  }
  15.     }
  16.     public string ReadKey
  17.     {
  18.         get { return _readKey; }
  19.     }
  20.     public string ChannelId
  21.     {
  22.        get { return _channelId; }
  23.     }
  24.     public void SetWriteKey(string keyVal)
  25.     {
  26.         writeKey = keyVal;
  27.     }
  28.     public void SetReadKey(string keyVal)
  29.     {
  30.         readKey = keyVal;
  31.     }
  32.     public void SetChannelId(string channelIdVal)
  33.     {
  34.        _channelId = channelIdVal;
  35.     }
  36.     public string GetUpdateDataString(byte fieldNumber, int valueToWrite)
  37.     {
  38.         return strUpdateBase + " ? key=" + _writeKey + "&field" + fieldNumber.ToString() + "=";
  39.     }
  40.     public string GetReadFeedString(byte numberOfDataToRead)
  41.     {
  42.         return strReadData + _channelId +
  43.                     strReadFeed_SecondPart + _readKey + "&results" + "=" + numberOfDataToRead;
  44.     }
  45.     public string GetReadFieldString(byte fieldNumber, byte numberOfDataToRead)
  46.     {
  47.         return strReadData + _channelId +
  48.                     strFields + fieldNumber.ToString() + strFields_2 + _readKey + "&results" + "=" + numberOfDataToRead;
  49.     }
  50.     public string GetReaddUpdateHistoryString(string channelID)
  51.     {
  52.         return strReadData + _channelId + "/status.json?api_key=" + _readKey;
  53.     }
  54.     public string SendRequest(string requestToSend)
  55.     {
  56.         string readedData = "";
  57.         try
  58.         {
  59.             HttpWebRequest ThingsSpeakReq;
  60.             HttpWebResponse ThingsSpeakResp;
  61.             ThingsSpeakReq = (HttpWebRequest)WebRequest.Create(requestToSend);
  62.             ThingsSpeakReq.Method = WebRequestMethods.Http.Get;
  63.             ThingsSpeakReq.Accept = "application/json";
  64.             ThingsSpeakResp = (HttpWebResponse)ThingsSpeakReq.GetResponse();
  65.             using (Stream responseStream = ThingsSpeakResp.GetResponseStream())
  66.             {
  67.                 StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
  68.                 readedData = (reader.ReadToEnd());
  69.             }
  70.             if (!(string.Equals(ThingsSpeakResp.StatusDescription"OK")))
  71.             {
  72.                 Exception exData = new Exception(ThingsSpeakResp.StatusDescription);
  73.                 return exData.ToString();
  74.             }
  75.         }
  76.         catch (Exception ex)
  77.         {
  78.             return ex.ToString();
  79.         }
  80.         return readedData;
  81.     }
  82. }