{"id":1866,"date":"2015-01-01T20:37:37","date_gmt":"2015-01-01T20:37:37","guid":{"rendered":"http:\/\/41j.com\/blog\/?p=1866"},"modified":"2015-01-17T03:02:22","modified_gmt":"2015-01-17T03:02:22","slug":"posting-safecast-api-esp8266","status":"publish","type":"post","link":"https:\/\/41j.com\/blog\/2015\/01\/posting-safecast-api-esp8266\/","title":{"rendered":"Posting to the Safecast API from the esp8266"},"content":{"rendered":"<p>As you may know <a href=\"www.safecast.org\">Safecast<\/a> run a community driven radiation monitoring network. I&#8217;ve been looking at using a esp8266 to upload radiation readings from a Geiger counter. The Safecast API is well documented <a href=\"https:\/\/api.safecast.org\/\">here<\/a>. And uses simple JSON post requests to send measurements. Before starting you need to login and obtain an API key. The API key authenticates you and allows you to upload measurements.<\/p>\n<p>It doesn&#8217;t make sense to implement a whole HTTP client on the esp8266, so I&#8217;m just posting the data over a standard TCP connection. It&#8217;s also possible to post measurements using curl, e.g.:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ncurl &quot;http:\/\/dev.safecast.org\/measurements.json?api_key=YOURKEY&quot; -v -X POST -H 'User-Agent: Arduino' -H &quot;Content-Type: application\/json&quot; -d '{&quot;longitude&quot;:&quot;119.7447&quot;,&quot;latitude&quot;:&quot;32.6617&quot;,&quot;device_id&quot;:&quot;47&quot;,&quot;value&quot;:&quot;6&quot;,&quot;unit&quot;:&quot;cpm&quot;}'\r\n<\/pre>\n<p>I grabbed this season with wireshark, here&#8217;s what the client side transmission looks like:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nPOST \/measurements.json?api_key=YOURKEY HTTP\/1.1\r\nHost: dev.safecast.org\r\nAccept: *\/*\r\nUser-Agent: Arduino\r\nContent-Type: application\/json\r\nContent-Length: 88\r\n\r\n{&quot;longitude&quot;:&quot;119.7447&quot;,&quot;latitude&quot;:&quot;32.6617&quot;,&quot;device_id&quot;:&quot;47&quot;,&quot;value&quot;:&quot;63&quot;,&quot;unit&quot;:&quot;cpm&quot;}\r\n<\/pre>\n<p>The ESP8266 code posts to the API and dumps the returned tcp data over serial for example (I&#8217;ve randomized the session keys):<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nIP: 192.168.0.8looklokkDST: 107.161.162.75conncendsentrecvHTTP\/1.1 201 Created\r\nContent-Type: application\/json; charset=utf-8\r\nTransfer-Encoding: chunked\r\nConnection: keep-alive\r\nStatus: 201 Created\r\nLocation: http:\/\/dev.safecast.org\/en-US\/measurements\/161114\r\nX-UA-Compatible: IE=Edge\r\nETag: &quot;73984732984feafefe76876&quot;\r\nCache-Control: max-age=0, private, must-revalidate\r\nSet-Cookie: _safecast_session=UISAHFUIHAFiudhfuishfiudshfdsuihfsiUfihdaFUIDHFDS\r\nX-Runtime: 0.066836\r\nX-Powered-By: Phusion Passenger 4.0.53\r\nDate: Thu, 01 Jan 2015 11:27:25 GMT\r\nServer: nginx\/1.6.2 + Phusion Passenger 4.0.53\r\n\r\nrecva9\r\n{&quot;captured_at&quot;:null,&quot;device_id&quot;:47,&quot;id&quot;:161114,&quot;location_name&quot;:null,&quot;original_id&quot;:161114,&quot;unit&quot;:&quot;c}\r\nrecv0\r\n<\/pre>\n<p>If everything worked, you should see the submission when you login e.g.<\/p>\n<p><a href=\"http:\/\/41j.com\/blog\/wp-content\/uploads\/2015\/01\/submission_safecast.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/41j.com\/blog\/wp-content\/uploads\/2015\/01\/submission_safecast.png\" alt=\"submission_safecast\" width=\"529\" height=\"93\" class=\"aligncenter size-full wp-image-1869\" srcset=\"https:\/\/41j.com\/blog\/wp-content\/uploads\/2015\/01\/submission_safecast.png 529w, https:\/\/41j.com\/blog\/wp-content\/uploads\/2015\/01\/submission_safecast-300x53.png 300w\" sizes=\"auto, (max-width: 529px) 100vw, 529px\" \/><\/a><\/p>\n<p>The code is below, it&#8217;s a modification of that <a href=\"http:\/\/41j.com\/blog\/2015\/01\/esp8266-client-mode-connect-remote-host-simple-example\/\">shown yesterday<\/a>:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &quot;ets_sys.h&quot;\r\n#include &quot;osapi.h&quot;\r\n#include &quot;gpio.h&quot;\r\n#include &quot;os_type.h&quot;\r\n#include &quot;user_config.h&quot;\r\n#include &quot;user_interface.h&quot;\r\n#include &quot;uart.h&quot;\r\n\r\n#include &quot;c_types.h&quot;\r\n#include &quot;espconn.h&quot;\r\n#include &quot;mem.h&quot;\/\r\n\/\/#include &quot;user_network.h&quot;\r\n\/\/#include &quot;user_display.h&quot;\r\n\r\n#define user_procTaskPrio        0\r\n#define user_procTaskQueueLen    1\r\nos_event_t    user_procTaskQueue&#x5B;user_procTaskQueueLen];\r\nstatic void loop(os_event_t *events);\r\n\r\n\/\/Main code function\r\nstatic void ICACHE_FLASH_ATTR\r\nloop(os_event_t *events)\r\n{\r\n    os_printf(&quot;Hello\\n\\r&quot;);\r\n    os_delay_us(10000);\r\n    system_os_post(user_procTaskPrio, 0, 0 );\r\n}\r\n\r\nstatic void ICACHE_FLASH_ATTR networkConnectedCb(void *arg);\r\nstatic void ICACHE_FLASH_ATTR networkDisconCb(void *arg);\r\nstatic void ICACHE_FLASH_ATTR networkReconCb(void *arg, sint8 err);\r\nstatic void ICACHE_FLASH_ATTR networkRecvCb(void *arg, char *data, unsigned short len);\r\nstatic void ICACHE_FLASH_ATTR networkSentCb(void *arg);\r\nvoid ICACHE_FLASH_ATTR network_init();\r\n\r\nLOCAL os_timer_t network_timer;\r\n\r\nstatic void ICACHE_FLASH_ATTR networkServerFoundCb(const char *name, ip_addr_t *ip, void *arg) {\r\n  static esp_tcp tcp;\r\n  struct espconn *conn=(struct espconn *)arg;\r\n  if (ip==NULL) {\r\n    os_printf(&quot;Nslookup failed :\/ Trying again...\\n&quot;);\r\n    uart0_tx_buffer(&quot;lfai&quot;,4);\r\n    network_init();\r\n  }\r\n\r\n  uart0_tx_buffer(&quot;lokk&quot;,4);\r\n  char page_buffer&#x5B;20];\r\n  os_sprintf(page_buffer,&quot;DST: %d.%d.%d.%d&quot;,\r\n  *((uint8 *)&amp;ip-&gt;addr), *((uint8 *)&amp;ip-&gt;addr + 1),\r\n  *((uint8 *)&amp;ip-&gt;addr + 2), *((uint8 *)&amp;ip-&gt;addr + 3));\r\n  uart0_tx_buffer(page_buffer,strlen(page_buffer));\r\n\r\n  conn-&gt;type=ESPCONN_TCP;\r\n  conn-&gt;state=ESPCONN_NONE;\r\n  conn-&gt;proto.tcp=&amp;tcp;\r\n  conn-&gt;proto.tcp-&gt;local_port=espconn_port();\r\n  conn-&gt;proto.tcp-&gt;remote_port=80;\r\n  os_memcpy(conn-&gt;proto.tcp-&gt;remote_ip, &amp;ip-&gt;addr, 4);\r\n  espconn_regist_connectcb(conn, networkConnectedCb);\r\n  espconn_regist_disconcb(conn, networkDisconCb);\r\n  espconn_regist_reconcb(conn, networkReconCb);\r\n  espconn_regist_recvcb(conn, networkRecvCb);\r\n  espconn_regist_sentcb(conn, networkSentCb);\r\n  espconn_connect(conn);\r\n}\r\n\r\nstatic void ICACHE_FLASH_ATTR networkSentCb(void *arg) {\r\n  uart0_tx_buffer(&quot;sent&quot;,4);\r\n}\r\n\r\nstatic void ICACHE_FLASH_ATTR networkRecvCb(void *arg, char *data, unsigned short len) {\r\n\r\n  uart0_tx_buffer(&quot;recv&quot;,4);\r\n  \r\n  struct espconn *conn=(struct espconn *)arg;\r\n  int x;\r\n  uart0_tx_buffer(data,len);\r\n}\r\n\r\nstatic void ICACHE_FLASH_ATTR networkConnectedCb(void *arg) {\r\n\r\n  uart0_tx_buffer(&quot;conn&quot;,4);\r\n  struct espconn *conn=(struct espconn *)arg;\r\n\r\n  char transmission&#x5B;1024];\r\n\r\n  char *header = &quot;POST \/measurements.json?api_key=YOURKEY HTTP\/1.1\\r\\n&quot;\r\n                 &quot;Host: dev.safecast.org\\r\\n&quot;\r\n                 &quot;Accept: *\/*\\r\\n&quot;\r\n                 &quot;User-Agent: Arduino\\r\\n&quot;\r\n                 &quot;Content-Type: application\/json\\r\\n&quot;\r\n                 &quot;Content-Length: &quot;;\r\n \r\n  char *json = &quot;{\\&quot;longitude\\&quot;:\\&quot;111.1111\\&quot;,\\&quot;latitude\\&quot;:\\&quot;11.1111\\&quot;,\\&quot;device_id\\&quot;:\\&quot;47\\&quot;,\\&quot;value\\&quot;:\\&quot;63\\&quot;,\\&quot;unit\\&quot;:\\&quot;cpm\\&quot;}&quot;;\r\n \r\n  int head_len = strlen(header);\r\n  int json_len = strlen(json);\r\n\r\n  char json_len_str&#x5B;10];\r\n  os_sprintf(json_len_str,&quot;%d&quot;,json_len);\r\n  int json_len_str_len = strlen(json_len_str);\r\n\r\n  strcpy(transmission,header);\r\n  strcpy(transmission+head_len,json_len_str);\r\n  strcpy(transmission+head_len+json_len_str_len,&quot;\\r\\n\\r\\n&quot;);\r\n  strcpy(transmission+head_len+json_len_str_len+4,json);\r\n\r\n  sint8 d = espconn_sent(conn,transmission,strlen(transmission));\r\n\r\n  espconn_regist_recvcb(conn, networkRecvCb);\r\n  uart0_tx_buffer(&quot;cend&quot;,4);\r\n}\r\n\r\nstatic void ICACHE_FLASH_ATTR networkReconCb(void *arg, sint8 err) {\r\n  uart0_tx_buffer(&quot;rcon&quot;,4);\r\n}\r\n\r\nstatic void ICACHE_FLASH_ATTR networkDisconCb(void *arg) {\r\n  uart0_tx_buffer(&quot;dcon&quot;,4);\r\n}\r\n\r\n\r\nvoid ICACHE_FLASH_ATTR network_start() {\r\n  static struct espconn conn;\r\n  static ip_addr_t ip;\r\n  os_printf(&quot;Looking up server...\\n&quot;);\r\n    uart0_tx_buffer(&quot;look&quot;,4);\r\n  espconn_gethostbyname(&amp;conn, &quot;dev.safecast.org&quot;, &amp;ip, networkServerFoundCb);\r\n}\r\n\r\nvoid ICACHE_FLASH_ATTR network_check_ip(void) {\r\n  struct ip_info ipconfig;\r\n  os_timer_disarm(&amp;network_timer);\r\n  wifi_get_ip_info(STATION_IF, &amp;ipconfig);\r\n  if (wifi_station_get_connect_status() == STATION_GOT_IP &amp;&amp; ipconfig.ip.addr != 0) {\r\n    char page_buffer&#x5B;20];\r\n    os_sprintf(page_buffer,&quot;IP: %d.%d.%d.%d&quot;,IP2STR(&amp;ipconfig.ip));\r\n    uart0_tx_buffer(page_buffer,strlen(page_buffer));\r\n    network_start();\r\n  } else {\r\n    os_printf(&quot;No ip found\\n\\r&quot;);\r\n    os_timer_setfn(&amp;network_timer, (os_timer_func_t *)network_check_ip, NULL);\r\n    os_timer_arm(&amp;network_timer, 1000, 0);\r\n  }\r\n}\r\n\r\nvoid ICACHE_FLASH_ATTR network_init() {\r\n  os_timer_disarm(&amp;network_timer);\r\n  os_timer_setfn(&amp;network_timer, (os_timer_func_t *)network_check_ip, NULL);\r\n  os_timer_arm(&amp;network_timer, 1000, 0);\r\n}\r\n\r\n\/\/Init function \r\nvoid ICACHE_FLASH_ATTR user_init() {\r\n\r\n    uart_init(BIT_RATE_9600,BIT_RATE_9600);\r\n    char ssid&#x5B;32] = SSID;\r\n    char password&#x5B;64] = SSID_PASSWORD;\r\n    struct station_config stationConf;\r\n\r\n    \/\/Set station mode\r\n    wifi_set_opmode( 0x1 );\r\n\r\n    \/\/Set ap settings\r\n    os_memcpy(&amp;stationConf.ssid, ssid, 32);\r\n    os_memcpy(&amp;stationConf.password, password, 64);\r\n    wifi_station_set_config(&amp;stationConf);\r\n\r\n    uart0_tx_buffer(&quot;init&quot;,4);\r\n\r\n    \/\/Start os task\r\n    system_os_task(loop, user_procTaskPrio,user_procTaskQueue, user_procTaskQueueLen);\r\n\r\n    system_os_post(user_procTaskPrio, 0, 0 );\r\n\r\n    network_init();\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>As you may know Safecast run a community driven radiation monitoring network. I&#8217;ve been looking at using a esp8266 to upload radiation readings from a Geiger counter. The Safecast API is well documented here. And uses simple JSON post requests to send measurements. Before starting you need to login and obtain an API key. The [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1],"tags":[],"class_list":["post-1866","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1RRoU-u6","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/1866","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/comments?post=1866"}],"version-history":[{"count":3,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/1866\/revisions"}],"predecessor-version":[{"id":1870,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/1866\/revisions\/1870"}],"wp:attachment":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/media?parent=1866"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/categories?post=1866"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/tags?post=1866"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}