{"action":"created","comment":{"url":"https://api.github.com/repos/xively/xively-client-c/pulls/comments/151662830","pull_request_review_id":77406429,"id":151662830,"diff_hunk":"@@ -0,0 +1,307 @@\n+/* Copyright (c) 2003-2017, LogMeIn, Inc. All rights reserved.\n+ *\n+ * This is part of the Xively C Client library,\n+ * it is licensed under the BSD 3-Clause license.\n+ */\n+\n+#include <sys/stat.h>\n+#include <errno.h>\n+#include <assert.h>\n+#include <string.h>\n+#include <stdio.h>\n+\n+#include <xi_bsp_debug.h>\n+#include <xi_bsp_io_fs.h>\n+#include <xi_bsp_fwu.h>\n+#include <xi_bsp_mem.h>\n+\n+#include \"esp_vfs.h\"\n+#include \"esp_vfs_fat.h\"\n+#include \"esp_ota_ops.h\"\n+\n+#define XI_ESP32_MAX_FILENAME_LEN 64\n+#define ESP32_FS_BASE_PATH \"/spiflash\"\n+\n+#define XI_BSP_IO_FS_CHECK_CND( cnd, e, s )                                              \\\n+    if ( ( cnd ) )                                                                       \\\n+    {                                                                                    \\\n+        ( s ) = ( e );                                                                   \\\n+        perror( \"Xi FS BSP errno\" );                                                     \\\n+        goto err_handling;                                                               \\\n+    }\n+\n+/* These functions are implemented in the application layer to report status */\n+extern void esp32_xibsp_notify_update_started( const char* filename, size_t file_size );\n+extern void esp32_xibsp_notify_chunk_written( size_t chunk_size, size_t offset );\n+\n+/* The size of the buffer to be used for reads. */\n+const size_t xi_bsp_io_fs_buffer_size = 1024;\n+/* ESP32 Over The Air FW updates API handle */\n+static esp_ota_handle_t open_firmware_bin_handle = 0;\n+\n+/* translates bsp errno errors to the xi_bsp_io_fs_state_t values */\n+xi_bsp_io_fs_state_t xi_bsp_io_fs_posix_errno_2_xi_bsp_io_fs_state( int errno_value )\n+{\n+    xi_bsp_io_fs_state_t ret = XI_BSP_IO_FS_STATE_OK;\n+\n+    switch ( errno_value )\n+    {\n+        case EBADF:\n+        case EACCES:\n+        case EFAULT:\n+        case ENOENT:\n+        case ENOTDIR:\n+            ret = XI_BSP_IO_FS_RESOURCE_NOT_AVAILABLE;\n+            break;\n+        case ELOOP:\n+        case EOVERFLOW:\n+            ret = XI_BSP_IO_FS_ERROR;\n+            break;\n+        case EINVAL:\n+            ret = XI_BSP_IO_FS_INVALID_PARAMETER;\n+            break;\n+        case ENAMETOOLONG:\n+            ret = XI_BSP_IO_FS_INVALID_PARAMETER;\n+            break;\n+        case ENOMEM:\n+            ret = XI_BSP_IO_FS_OUT_OF_MEMORY;\n+            break;\n+        default:\n+            ret = XI_BSP_IO_FS_ERROR;\n+    }\n+\n+    return ret;\n+}\n+\n+xi_bsp_io_fs_state_t\n+xi_bsp_io_fs_open( const char* const resource_name,\n+                   const size_t size,\n+                   const xi_bsp_io_fs_open_flags_t open_flags,\n+                   xi_bsp_io_fs_resource_handle_t* resource_handle_out )\n+{\n+    char filepath[XI_ESP32_MAX_FILENAME_LEN] = \"\";\n+    xi_bsp_io_fs_state_t ret                 = XI_BSP_IO_FS_STATE_OK;\n+    esp_err_t esp_retv                       = ESP_OK;\n+    FILE* fp                                 = NULL;\n+\n+    if ( NULL == resource_name || NULL == resource_handle_out )\n+    {\n+        return XI_BSP_IO_FS_INVALID_PARAMETER;\n+    }\n+\n+    if ( xi_bsp_fwu_is_this_firmware( resource_name ) )\n+    {\n+        const esp_partition_t* next_partition = esp_ota_get_next_update_partition( NULL );\n+\n+        if ( NULL == next_partition )\n+        {\n+            xi_bsp_debug_logger( \"Failed to get next OTA update partition\" );\n+            return XI_BSP_IO_FS_OPEN_ERROR;\n+        }\n+\n+        esp_retv = esp_ota_begin( next_partition, size,\n+                                  ( esp_ota_handle_t* )resource_handle_out );\n+\n+        if ( ESP_OK != esp_retv )\n+        {\n+            xi_bsp_debug_format( \"esp_ota_begin() failed with error %d\", esp_retv );\n+            return XI_BSP_IO_FS_OPEN_ERROR;\n+        }\n+\n+        esp32_xibsp_notify_update_started( resource_name, size );\n+        open_firmware_bin_handle = *resource_handle_out;\n+    }\n+    else\n+    {\n+        snprintf( filepath, XI_ESP32_MAX_FILENAME_LEN, \"%s/%s\", ESP32_FS_BASE_PATH,\n+                  resource_name );\n+        fp = fopen( ( const char* )filepath,\n+                    ( open_flags & XI_BSP_IO_FS_OPEN_READ ) ? \"rb\" : \"wb\" );\n+\n+        /* if error on fopen check the errno value */\n+        XI_BSP_IO_FS_CHECK_CND(\n+            NULL == fp, xi_bsp_io_fs_posix_errno_2_xi_bsp_io_fs_state( errno ), ret );\n+\n+        *resource_handle_out = ( xi_bsp_io_fs_resource_handle_t )fp;\n+\n+        return XI_BSP_IO_FS_STATE_OK;\n+    }\n+\n+    return XI_BSP_IO_FS_STATE_OK;\n+\n+err_handling:\n+    return ret;\n+}\n+\n+xi_bsp_io_fs_state_t\n+xi_bsp_io_fs_write( const xi_bsp_io_fs_resource_handle_t resource_handle,\n+                    const uint8_t* const buffer,\n+                    const size_t buffer_size,\n+                    const size_t offset,\n+                    size_t* const bytes_written )\n+{\n+    ( void )offset;","path":"src/bsp/platform/esp32/xi_bsp_io_fs_esp32.c","position":143,"original_position":143,"commit_id":"75ee6db357611b6ff3588b93cb28073f9669b5af","original_commit_id":"75ee6db357611b6ff3588b93cb28073f9669b5af","user":{"login":"atigyi","id":7406739,"avatar_url":"https://avatars0.githubusercontent.com/u/7406739?v=4","gravatar_id":"","url":"https://api.github.com/users/atigyi","html_url":"https://github.com/atigyi","followers_url":"https://api.github.com/users/atigyi/followers","following_url":"https://api.github.com/users/atigyi/following{/other_user}","gists_url":"https://api.github.com/users/atigyi/gists{/gist_id}","starred_url":"https://api.github.com/users/atigyi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/atigyi/subscriptions","organizations_url":"https://api.github.com/users/atigyi/orgs","repos_url":"https://api.github.com/users/atigyi/repos","events_url":"https://api.github.com/users/atigyi/events{/privacy}","received_events_url":"https://api.github.com/users/atigyi/received_events","type":"User","site_admin":false},"body":"offset is used, this line can be nuked to orbit","created_at":"2017-11-17T11:24:46Z","updated_at":"2017-11-17T11:24:46Z","html_url":"https://github.com/xively/xively-client-c/pull/160#discussion_r151662830","pull_request_url":"https://api.github.com/repos/xively/xively-client-c/pulls/160","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/xively/xively-client-c/pulls/comments/151662830"},"html":{"href":"https://github.com/xively/xively-client-c/pull/160#discussion_r151662830"},"pull_request":{"href":"https://api.github.com/repos/xively/xively-client-c/pulls/160"}}},"pull_request":{"url":"https://api.github.com/repos/xively/xively-client-c/pulls/160","id":148600732,"html_url":"https://github.com/xively/xively-client-c/pull/160","diff_url":"https://github.com/xively/xively-client-c/pull/160.diff","patch_url":"https://github.com/xively/xively-client-c/pull/160.patch","issue_url":"https://api.github.com/repos/xively/xively-client-c/issues/160","number":160,"state":"open","locked":false,"title":"Feature/ESP32 Over The Air firmware updates","user":{"login":"Palantir555","id":777208,"avatar_url":"https://avatars2.githubusercontent.com/u/777208?v=4","gravatar_id":"","url":"https://api.github.com/users/Palantir555","html_url":"https://github.com/Palantir555","followers_url":"https://api.github.com/users/Palantir555/followers","following_url":"https://api.github.com/users/Palantir555/following{/other_user}","gists_url":"https://api.github.com/users/Palantir555/gists{/gist_id}","starred_url":"https://api.github.com/users/Palantir555/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Palantir555/subscriptions","organizations_url":"https://api.github.com/users/Palantir555/orgs","repos_url":"https://api.github.com/users/Palantir555/repos","events_url":"https://api.github.com/users/Palantir555/events{/privacy}","received_events_url":"https://api.github.com/users/Palantir555/received_events","type":"User","site_admin":false},"body":"[ Description ]\r\n\r\nFix SFT use-after-free bug. A file was being closed before its read buffer was copied, which caused occasional buffer corruptions.\r\n\r\nWorking OTA firmware updates for the ESP32:\r\n\r\n1. Implemented the ESP32's file and fwu BSPs\r\n2. Implement sdkconfig.defaults with default project configuration\r\n3. Create a partition for a FAT Filesystem, to be consumed by the SFT logic\r\n4. Updated the example to use OTA firmware updates\r\n5. Updated ESP32 preset to enable the control topic and SFT\r\n6. Updated the example to initialize the Xively Client *after* the device is connected to WiFi, so the SNTP process isn't started any earlier\r\n7. Improve timing of the GPIO button debouncer\r\n8. Fixed an 'unused variable' warning in the protobuf code that caused the libxively.a build to fail\r\n\r\n[ JIRA ]\r\nESP32 SFT Implementation\r\nhttps://jira.3amlabs.net/browse/XCL-3080\r\n\r\n[ Reviewer ]\r\n@atigyi \r\n\r\n[ QA ]\r\nMy device successfully updates its firmware. I've tried multiple times, disconnected the device while it downloaded the new image, and other manual testing. Everything seems to be working correctly.\r\n\r\nSometimes, the ESP32's API fails to write the last chunk in a firmware update. The issue can be resolved running `make flash_erase` and re-flashing the device.\r\n\r\n[ Release Notes ]\r\nESP32 now supports firmware updates over MQTT using Xively's Secure File Transfer protocol.","created_at":"2017-10-25T08:28:11Z","updated_at":"2017-11-17T11:24:46Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5361d46678658fd8df71b94500687909c216ec3d","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/xively/xively-client-c/pulls/160/commits","review_comments_url":"https://api.github.com/repos/xively/xively-client-c/pulls/160/comments","review_comment_url":"https://api.github.com/repos/xively/xively-client-c/pulls/comments{/number}","comments_url":"https://api.github.com/repos/xively/xively-client-c/issues/160/comments","statuses_url":"https://api.github.com/repos/xively/xively-client-c/statuses/75ee6db357611b6ff3588b93cb28073f9669b5af","head":{"label":"xively:feat/esp32_fw_updates","ref":"feat/esp32_fw_updates","sha":"75ee6db357611b6ff3588b93cb28073f9669b5af","user":{"login":"xively","id":4222960,"avatar_url":"https://avatars1.githubusercontent.com/u/4222960?v=4","gravatar_id":"","url":"https://api.github.com/users/xively","html_url":"https://github.com/xively","followers_url":"https://api.github.com/users/xively/followers","following_url":"https://api.github.com/users/xively/following{/other_user}","gists_url":"https://api.github.com/users/xively/gists{/gist_id}","starred_url":"https://api.github.com/users/xively/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xively/subscriptions","organizations_url":"https://api.github.com/users/xively/orgs","repos_url":"https://api.github.com/users/xively/repos","events_url":"https://api.github.com/users/xively/events{/privacy}","received_events_url":"https://api.github.com/users/xively/received_events","type":"Organization","site_admin":false},"repo":{"id":65734711,"name":"xively-client-c","full_name":"xively/xively-client-c","owner":{"login":"xively","id":4222960,"avatar_url":"https://avatars1.githubusercontent.com/u/4222960?v=4","gravatar_id":"","url":"https://api.github.com/users/xively","html_url":"https://github.com/xively","followers_url":"https://api.github.com/users/xively/followers","following_url":"https://api.github.com/users/xively/following{/other_user}","gists_url":"https://api.github.com/users/xively/gists{/gist_id}","starred_url":"https://api.github.com/users/xively/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xively/subscriptions","organizations_url":"https://api.github.com/users/xively/orgs","repos_url":"https://api.github.com/users/xively/repos","events_url":"https://api.github.com/users/xively/events{/privacy}","received_events_url":"https://api.github.com/users/xively/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/xively/xively-client-c","description":"The official and highly portable Xively C Client designed for embedded platforms.","fork":false,"url":"https://api.github.com/repos/xively/xively-client-c","forks_url":"https://api.github.com/repos/xively/xively-client-c/forks","keys_url":"https://api.github.com/repos/xively/xively-client-c/keys{/key_id}","collaborators_url":"https://api.github.com/repos/xively/xively-client-c/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/xively/xively-client-c/teams","hooks_url":"https://api.github.com/repos/xively/xively-client-c/hooks","issue_events_url":"https://api.github.com/repos/xively/xively-client-c/issues/events{/number}","events_url":"https://api.github.com/repos/xively/xively-client-c/events","assignees_url":"https://api.github.com/repos/xively/xively-client-c/assignees{/user}","branches_url":"https://api.github.com/repos/xively/xively-client-c/branches{/branch}","tags_url":"https://api.github.com/repos/xively/xively-client-c/tags","blobs_url":"https://api.github.com/repos/xively/xively-client-c/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/xively/xively-client-c/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/xively/xively-client-c/git/refs{/sha}","trees_url":"https://api.github.com/repos/xively/xively-client-c/git/trees{/sha}","statuses_url":"https://api.github.com/repos/xively/xively-client-c/statuses/{sha}","languages_url":"https://api.github.com/repos/xively/xively-client-c/languages","stargazers_url":"https://api.github.com/repos/xively/xively-client-c/stargazers","contributors_url":"https://api.github.com/repos/xively/xively-client-c/contributors","subscribers_url":"https://api.github.com/repos/xively/xively-client-c/subscribers","subscription_url":"https://api.github.com/repos/xively/xively-client-c/subscription","commits_url":"https://api.github.com/repos/xively/xively-client-c/commits{/sha}","git_commits_url":"https://api.github.com/repos/xively/xively-client-c/git/commits{/sha}","comments_url":"https://api.github.com/repos/xively/xively-client-c/comments{/number}","issue_comment_url":"https://api.github.com/repos/xively/xively-client-c/issues/comments{/number}","contents_url":"https://api.github.com/repos/xively/xively-client-c/contents/{+path}","compare_url":"https://api.github.com/repos/xively/xively-client-c/compare/{base}...{head}","merges_url":"https://api.github.com/repos/xively/xively-client-c/merges","archive_url":"https://api.github.com/repos/xively/xively-client-c/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/xively/xively-client-c/downloads","issues_url":"https://api.github.com/repos/xively/xively-client-c/issues{/number}","pulls_url":"https://api.github.com/repos/xively/xively-client-c/pulls{/number}","milestones_url":"https://api.github.com/repos/xively/xively-client-c/milestones{/number}","notifications_url":"https://api.github.com/repos/xively/xively-client-c/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/xively/xively-client-c/labels{/name}","releases_url":"https://api.github.com/repos/xively/xively-client-c/releases{/id}","deployments_url":"https://api.github.com/repos/xively/xively-client-c/deployments","created_at":"2016-08-15T13:35:30Z","updated_at":"2017-10-11T15:06:55Z","pushed_at":"2017-11-16T14:26:37Z","git_url":"git://github.com/xively/xively-client-c.git","ssh_url":"git@github.com:xively/xively-client-c.git","clone_url":"https://github.com/xively/xively-client-c.git","svn_url":"https://github.com/xively/xively-client-c","homepage":"","size":15485,"stargazers_count":11,"watchers_count":11,"language":"C","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":6,"mirror_url":null,"archived":false,"open_issues_count":3,"forks":6,"open_issues":3,"watchers":11,"default_branch":"master"}},"base":{"label":"xively:development","ref":"development","sha":"a06722ada6cae90d81e138a33e677aa6586b49cd","user":{"login":"xively","id":4222960,"avatar_url":"https://avatars1.githubusercontent.com/u/4222960?v=4","gravatar_id":"","url":"https://api.github.com/users/xively","html_url":"https://github.com/xively","followers_url":"https://api.github.com/users/xively/followers","following_url":"https://api.github.com/users/xively/following{/other_user}","gists_url":"https://api.github.com/users/xively/gists{/gist_id}","starred_url":"https://api.github.com/users/xively/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xively/subscriptions","organizations_url":"https://api.github.com/users/xively/orgs","repos_url":"https://api.github.com/users/xively/repos","events_url":"https://api.github.com/users/xively/events{/privacy}","received_events_url":"https://api.github.com/users/xively/received_events","type":"Organization","site_admin":false},"repo":{"id":65734711,"name":"xively-client-c","full_name":"xively/xively-client-c","owner":{"login":"xively","id":4222960,"avatar_url":"https://avatars1.githubusercontent.com/u/4222960?v=4","gravatar_id":"","url":"https://api.github.com/users/xively","html_url":"https://github.com/xively","followers_url":"https://api.github.com/users/xively/followers","following_url":"https://api.github.com/users/xively/following{/other_user}","gists_url":"https://api.github.com/users/xively/gists{/gist_id}","starred_url":"https://api.github.com/users/xively/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xively/subscriptions","organizations_url":"https://api.github.com/users/xively/orgs","repos_url":"https://api.github.com/users/xively/repos","events_url":"https://api.github.com/users/xively/events{/privacy}","received_events_url":"https://api.github.com/users/xively/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/xively/xively-client-c","description":"The official and highly portable Xively C Client designed for embedded platforms.","fork":false,"url":"https://api.github.com/repos/xively/xively-client-c","forks_url":"https://api.github.com/repos/xively/xively-client-c/forks","keys_url":"https://api.github.com/repos/xively/xively-client-c/keys{/key_id}","collaborators_url":"https://api.github.com/repos/xively/xively-client-c/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/xively/xively-client-c/teams","hooks_url":"https://api.github.com/repos/xively/xively-client-c/hooks","issue_events_url":"https://api.github.com/repos/xively/xively-client-c/issues/events{/number}","events_url":"https://api.github.com/repos/xively/xively-client-c/events","assignees_url":"https://api.github.com/repos/xively/xively-client-c/assignees{/user}","branches_url":"https://api.github.com/repos/xively/xively-client-c/branches{/branch}","tags_url":"https://api.github.com/repos/xively/xively-client-c/tags","blobs_url":"https://api.github.com/repos/xively/xively-client-c/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/xively/xively-client-c/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/xively/xively-client-c/git/refs{/sha}","trees_url":"https://api.github.com/repos/xively/xively-client-c/git/trees{/sha}","statuses_url":"https://api.github.com/repos/xively/xively-client-c/statuses/{sha}","languages_url":"https://api.github.com/repos/xively/xively-client-c/languages","stargazers_url":"https://api.github.com/repos/xively/xively-client-c/stargazers","contributors_url":"https://api.github.com/repos/xively/xively-client-c/contributors","subscribers_url":"https://api.github.com/repos/xively/xively-client-c/subscribers","subscription_url":"https://api.github.com/repos/xively/xively-client-c/subscription","commits_url":"https://api.github.com/repos/xively/xively-client-c/commits{/sha}","git_commits_url":"https://api.github.com/repos/xively/xively-client-c/git/commits{/sha}","comments_url":"https://api.github.com/repos/xively/xively-client-c/comments{/number}","issue_comment_url":"https://api.github.com/repos/xively/xively-client-c/issues/comments{/number}","contents_url":"https://api.github.com/repos/xively/xively-client-c/contents/{+path}","compare_url":"https://api.github.com/repos/xively/xively-client-c/compare/{base}...{head}","merges_url":"https://api.github.com/repos/xively/xively-client-c/merges","archive_url":"https://api.github.com/repos/xively/xively-client-c/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/xively/xively-client-c/downloads","issues_url":"https://api.github.com/repos/xively/xively-client-c/issues{/number}","pulls_url":"https://api.github.com/repos/xively/xively-client-c/pulls{/number}","milestones_url":"https://api.github.com/repos/xively/xively-client-c/milestones{/number}","notifications_url":"https://api.github.com/repos/xively/xively-client-c/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/xively/xively-client-c/labels{/name}","releases_url":"https://api.github.com/repos/xively/xively-client-c/releases{/id}","deployments_url":"https://api.github.com/repos/xively/xively-client-c/deployments","created_at":"2016-08-15T13:35:30Z","updated_at":"2017-10-11T15:06:55Z","pushed_at":"2017-11-16T14:26:37Z","git_url":"git://github.com/xively/xively-client-c.git","ssh_url":"git@github.com:xively/xively-client-c.git","clone_url":"https://github.com/xively/xively-client-c.git","svn_url":"https://github.com/xively/xively-client-c","homepage":"","size":15485,"stargazers_count":11,"watchers_count":11,"language":"C","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":6,"mirror_url":null,"archived":false,"open_issues_count":3,"forks":6,"open_issues":3,"watchers":11,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/xively/xively-client-c/pulls/160"},"html":{"href":"https://github.com/xively/xively-client-c/pull/160"},"issue":{"href":"https://api.github.com/repos/xively/xively-client-c/issues/160"},"comments":{"href":"https://api.github.com/repos/xively/xively-client-c/issues/160/comments"},"review_comments":{"href":"https://api.github.com/repos/xively/xively-client-c/pulls/160/comments"},"review_comment":{"href":"https://api.github.com/repos/xively/xively-client-c/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/xively/xively-client-c/pulls/160/commits"},"statuses":{"href":"https://api.github.com/repos/xively/xively-client-c/statuses/75ee6db357611b6ff3588b93cb28073f9669b5af"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"atigyi"}}