├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── mos.yml └── src └── main.c /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Mongoose Shelly build 2 | on: 3 | push: 4 | branches: [ master ] 5 | jobs: 6 | release: 7 | name: Create a new release 8 | runs-on: ubuntu-latest 9 | outputs: 10 | upload_url: ${{ steps.create_release.outputs.upload_url }} 11 | steps: 12 | - name: Create Release 13 | uses: actions/create-release@v1 14 | id: create_release 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | with: 18 | tag_name: release-${{ github.run_id }} 19 | release_name: Release ${{ github.run_id }} 20 | draft: false 21 | prerelease: false 22 | build: 23 | name: Build for different Shelly models 24 | needs: release 25 | strategy: 26 | matrix: 27 | hwModel: [Shelly1, Shelly1L, Shelly1PM, ShellyPlugS, Shelly2, Shelly25, ShellyRGBW2, ShellyDimmer1, ShellyDimmer2, ShellyEM, ShellyBulb, ShellyVintage, ShellyPlugUS, ShellyHT, ShellyBulbDuo, ShellyI3] 28 | hwPlatform: [esp8266] 29 | targetFw: [tasmota] 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout code 33 | uses: actions/checkout@v2 34 | id: checkout_code 35 | - name: Build firmware 36 | uses: yaourdt/mongoose-os-action@v1.0.2 37 | with: 38 | mos-yml-path: . 39 | platform: ${{ matrix.hwPlatform }} 40 | build-var: MODEL=${{ matrix.hwModel }} --build-var TARGETFW=${{ matrix.targetFw }} 41 | id: mos_build 42 | - name: Upload Release Asset 43 | uses: actions/upload-release-asset@v1 44 | id: upload-release-asset 45 | env: 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 47 | with: 48 | upload_url: ${{ needs.release.outputs.upload_url }} 49 | asset_path: ./build/fw.zip 50 | asset_name: mg2${{ matrix.targetFw }}-${{ matrix.hwModel }}.zip 51 | asset_content_type: application/zip 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # mgos 4 | build/ 5 | 6 | # IDEs and editors 7 | .on-save.json 8 | auto_compile.log 9 | 10 | # System Files 11 | .DS_Store 12 | Thumbs.db 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | A minimal firmware for ota flashing tasmota from mongoose os 2 | Copyright (C) 2020, Mark Dornbach 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see https://www.gnu.org/licenses/. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mg2x 2 | 3 | A minimal firmware for OTA (over the air) flashing various target firmwares 4 | starting from Mongoose OS. 5 | 6 | Original repository at https://github.com/yaourdt/mgos-to-tasmota. This repository 7 | is tailored to use Tasmota Latest Release Version only. 8 | 9 | ## Overview 10 | 11 | Mg2x is an intermediate firmware that can be used to install [Tasmota](https://github.com/arendst/Tasmota) 12 | on various Shelly models. It will install the latest released version, and you 13 | can continue from there to your favourite target release. 14 | 15 | ## Install 16 | 17 | **Warning:** _This application should generally be safe to use for all supported 18 | devices. Still, overwriting a device's boot loader via OTA update is a risky 19 | operation. If something unexpected fails, your device may be bricked, unless you 20 | know how to flash a new firmware over a wired connection._ 21 | 22 | **Warning:** _You can go back to Mongoose OS via OTA as well, using [this firmware](https://github.com/yaourdt/tasmota-to-mgos), 23 | but be aware the application is still at an early stage. If something fails, 24 | your device may be bricked, if you don't know how to flash a new firmware over 25 | a wired connection._ 26 | 27 | Before flashing this firmware, connect your device to a WIFI network with 28 | internet access. From your browser, open the update URL for your device from the 29 | table below. Replace `shellyip` with the IP address of your Shelly. The device 30 | will restart one or two times and attempt to download Tasmota. If this download 31 | succeeds, the device will restart again, and you will see a new WIFI network 32 | labeled _tasmota-????_. This process should take no longer than 4 - 5 minutes, 33 | depending on your network connection. 34 | 35 | There is a [video tutorial](https://youtu.be/_oRr8FZyyQ0) on how to flash this 36 | firmware. Thank you, [digiblur](https://github.com/digiblur)! 37 | 38 | If the download fails, or your internet connection is disrupted, simply turn the 39 | device off and on again, the intermediate firmware will retry until it succeeds. 40 | 41 | In the unlikely event that the WIFI credentials are wrong, the device will try 42 | to connect to a backup WIFI with SSID _mgos-recover_ and password _RJoPuKC3u5_, 43 | which you can use for recovery. 44 | 45 | Device | Update URL | Tasmota Template 46 | --- | --- | --- 47 | Shelly 1 | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-Shelly1.zip` | `{"NAME":"Shelly 1","GPIO":[0,0,0,0,21,82,0,0,0,0,0,0,0],"FLAG":0,"BASE":46}` 48 | Shelly 1PM | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-Shelly1PM.zip` | `{"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18}` 49 | Shelly 1L | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-Shelly1L.zip` | `{"NAME":"Shelly 1L","GPIO":[320,0,0,0,192,224,0,0,0,0,193,0,0,4736],"FLAG":0,"BASE":18}` 50 | Shelly Plug S | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-ShellyPlugS.zip` | `{"NAME":"Shelly Plug S","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":2,"BASE":45}` 51 | Shelly 2 | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-Shelly2.zip` | `{"NAME":"Shelly 2","GPIO":[0,135,0,136,21,22,0,0,9,0,10,137,0],"FLAG":0,"BASE":47}` 52 | Shelly 2.5 | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-Shelly25.zip` | `{"NAME":"Shelly 2.5","GPIO":[56,0,17,0,21,83,0,0,6,82,5,22,156],"FLAG":2,"BASE":18}` 53 | Shelly RGBW2 | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-ShellyRGBW2.zip` | `{"NAME":"Shelly RGBW2","GPIO":[0,0,52,0,40,255,0,0,37,17,39,38,0],"FLAG":0,"BASE":18}` 54 | Shelly Dimmer 1 | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-ShellyDimmer1.zip` | `{"NAME":"Shelly Dimmer 1","GPIO":[0,3200,0,3232,5568,5600,0,0,192,0,193,288,0,4736],"FLAG":0,"BASE":18}` 55 | Shelly Dimmer 2 | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-ShellyDimmer2.zip` | `{"NAME":"Shelly Dimmer 2","GPIO":[0,3200,0,3232,5568,5600,0,0,193,0,192,0,320,4736],"FLAG":0,"BASE":18}` 56 | Shelly EM | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-ShellyEM.zip` | `{"NAME":"Shelly EM","GPIO":[0,0,0,0,0,0,0,0,6,156,5,21,0],"FLAG":15,"BASE":18}` 57 | Shelly Bulb | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-ShellyBulb.zip` | **not yet available, only flash if you a perfectly certain about what you are doing** 58 | Shelly Vintage | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-ShellyVintage.zip` | **not yet available, only flash if you a perfectly certain about what you are doing** 59 | Shelly Plug US | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-ShellyPlugUS.zip` | `{"NAME":"ShellyPlugUS","GPIO":[52,255,57,255,21,134,0,0,131,17,132,157,0],"FLAG":0,"BASE":45}` 60 | Shelly Duo | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-ShellyBulbDuo.zip` | `{"NAME":"Shelly Duo","GPIO":[0,0,0,0,38,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}` 61 | Shelly H&T | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-ShellyHT.zip` | **not yet available, only flash if you a perfectly certain about what you are doing** 62 | Shelly i3 | `http://shellyip/ota?url=http://ota.tasmota.com/tasmota/shelly/mg2tasmota-ShellyI3.zip` | `{"NAME":"Shelly i3","GPIO":[0,0,0,0,0,0,0,0,83,84,82,0,0],"FLAG":2,"BASE":18}` 63 | 64 | For your convenience, the table above also lists the matching Tasmota device 65 | templates from [templates.blakadder.com](https://templates.blakadder.com) which 66 | you can use to configure Tasmota after installation. 67 | 68 | ## Build the firmware yourself 69 | 70 | You can compile a binary version of this firmware using [mos tools](https://mongoose-os.com/docs/mongoose-os/quickstart/setup.md#1-download-and-install-mos-tool). Once installed, clone this repository and run 71 | `mos build --build-var MODEL=Shelly1 --build-var TARGETFW=tasmota --platform esp8266` 72 | to create a binary for e.g. a Shelly1 switch located in `build/fw.zip`. 73 | 74 | ## Acknowledgments 75 | Thanks to [rojer](https://github.com/rojer) for helping me with the debugging of 76 | the initial code. 77 | 78 | This firmware is build using a fork of [Mongoose OS docker action](https://github.com/dea82/mongoose-os-action) 79 | which can be found [here](https://github.com/yaourdt/mongoose-os-action). 80 | -------------------------------------------------------------------------------- /mos.yml: -------------------------------------------------------------------------------- 1 | author: mark dornbach 2 | description: a minimal firmware for ota flashing tasmota from mongoose os 3 | platform: esp8266 4 | version: 0.3.1 5 | 6 | libs_version: ${mos.version} 7 | modules_version: ${mos.version} 8 | mongoose_os_version: ${mos.version} 9 | manifest_version: 2020-01-29 10 | sources: [src] 11 | filesystem: [fs] 12 | tags: [c, tuya, tasmota, mongoose] 13 | 14 | # Custom configuration 15 | config_schema: 16 | #### sys #### 17 | - ["debug.level", 2] # 0 = ERROR, 1 = WARN, 2 = INFO, 3+ = DEBUG 18 | #### wifi #### 19 | - ["wifi.ap.enable", false ] 20 | - ["wifi.sta.enable", true ] # join wifi as client 21 | - ["sta_connect_timeout", 30 ] # timeout for wifi connection / seconds 22 | - ["wifi.sta1.ssid", "mgos-recover" ] # fallback SSID 23 | - ["wifi.sta1.pass", "RJoPuKC3u5" ] # fallback password 24 | - ["mg2x", "o", {"title": "Settings for this firmware"}] 25 | 26 | # External libs 27 | libs: 28 | - origin: https://github.com/mongoose-os-libs/boards # board definitions library 29 | - origin: https://github.com/mongoose-os-libs/ota-common # OTA support 30 | - origin: https://github.com/mongoose-os-libs/rpc-common # shared RPC API 31 | - origin: https://github.com/mongoose-os-libs/rpc-uart # RPC via serial console 32 | - origin: https://github.com/mongoose-os-libs/rpc-service-config # manage device configuration remotely 33 | - origin: https://github.com/mongoose-os-libs/ca-bundle # CA certificates 34 | 35 | conds: 36 | - when: build_vars.TARGETFW == "tasmota" 37 | apply: 38 | config_schema: 39 | - ["mg2x.url", "s", "https://ota.tasmota.com/tasmota/release/tasmota.bin", {"title": "URL of target firmware"}] 40 | - when: build_vars.MODEL == "Shelly1" 41 | apply: 42 | name: switch1 43 | build_vars: 44 | FS_SIZE: 262144 45 | FLASH_SIZE: 2097152 46 | BOOT_CONFIG_ADDR: 0x1000 47 | MGOS_ROOT_FS_TYPE: SPIFFS 48 | - when: build_vars.MODEL == "Shelly1PM" 49 | apply: 50 | name: switch1pm 51 | build_vars: 52 | FS_SIZE: 262144 53 | FLASH_SIZE: 2097152 54 | BOOT_CONFIG_ADDR: 0x1000 55 | MGOS_ROOT_FS_TYPE: SPIFFS 56 | - when: build_vars.MODEL == "Shelly1L" 57 | apply: 58 | name: switch1l 59 | build_vars: 60 | FS_SIZE: 262144 61 | FLASH_SIZE: 2097152 62 | BOOT_CONFIG_ADDR: 0x1000 63 | MGOS_ROOT_FS_TYPE: SPIFFS 64 | - when: build_vars.MODEL == "ShellyPlugS" 65 | apply: 66 | name: shelly-plug-s 67 | build_vars: 68 | FS_SIZE: 262144 69 | FLASH_SIZE: 2097152 70 | BOOT_CONFIG_ADDR: 0x7000 71 | MGOS_ROOT_FS_TYPE: SPIFFS 72 | - when: build_vars.MODEL == "Shelly2" 73 | apply: 74 | name: switch 75 | build_vars: 76 | FS_SIZE: 262144 77 | FLASH_SIZE: 2097152 78 | BOOT_CONFIG_ADDR: 0x1000 79 | MGOS_ROOT_FS_TYPE: SPIFFS 80 | - when: build_vars.MODEL == "Shelly25" 81 | apply: 82 | name: switch25 83 | build_vars: 84 | FS_SIZE: 262144 85 | FLASH_SIZE: 2097152 86 | BOOT_CONFIG_ADDR: 0x1000 87 | MGOS_ROOT_FS_TYPE: SPIFFS 88 | - when: build_vars.MODEL == "ShellyRGBW2" 89 | apply: 90 | name: rgbw2 91 | build_vars: 92 | FS_SIZE: 262144 93 | FLASH_SIZE: 2097152 94 | BOOT_CONFIG_ADDR: 0x7000 95 | MGOS_ROOT_FS_TYPE: SPIFFS 96 | - when: build_vars.MODEL == "ShellyDimmer1" 97 | apply: 98 | name: dimmer 99 | build_vars: 100 | FS_SIZE: 262144 101 | FLASH_SIZE: 2097152 102 | BOOT_CONFIG_ADDR: 0x7000 103 | MGOS_ROOT_FS_TYPE: SPIFFS 104 | - when: build_vars.MODEL == "ShellyDimmer2" 105 | apply: 106 | name: dimmer-l51 107 | build_vars: 108 | FS_SIZE: 262144 109 | FLASH_SIZE: 2097152 110 | BOOT_CONFIG_ADDR: 0x7000 111 | MGOS_ROOT_FS_TYPE: SPIFFS 112 | - when: build_vars.MODEL == "ShellyEM" 113 | apply: 114 | name: shellyem 115 | build_vars: 116 | FS_SIZE: 262144 117 | FLASH_SIZE: 2097152 118 | BOOT_CONFIG_ADDR: 0x1000 119 | MGOS_ROOT_FS_TYPE: SPIFFS 120 | - when: build_vars.MODEL == "ShellyBulb" 121 | apply: 122 | name: bulb 123 | build_vars: 124 | FS_SIZE: 262144 125 | FLASH_SIZE: 2097152 126 | BOOT_CONFIG_ADDR: 0x7000 127 | MGOS_ROOT_FS_TYPE: SPIFFS 128 | - when: build_vars.MODEL == "ShellyVintage" 129 | apply: 130 | name: bulb6w 131 | build_vars: 132 | FS_SIZE: 262144 133 | FLASH_SIZE: 2097152 134 | BOOT_CONFIG_ADDR: 0x7000 135 | MGOS_ROOT_FS_TYPE: SPIFFS 136 | - when: build_vars.MODEL == "ShellyPlugUS" 137 | apply: 138 | name: shelly-plug-u1 139 | build_vars: 140 | FS_SIZE: 262144 141 | FLASH_SIZE: 2097152 142 | BOOT_CONFIG_ADDR: 0x7000 143 | MGOS_ROOT_FS_TYPE: SPIFFS 144 | - when: build_vars.MODEL == "ShellyHT" 145 | apply: 146 | name: ht-sensor 147 | build_vars: 148 | FS_SIZE: 262144 149 | FLASH_SIZE: 2097152 150 | BOOT_CONFIG_ADDR: 0x7000 151 | MGOS_ROOT_FS_TYPE: SPIFFS 152 | - when: build_vars.MODEL == "ShellyBulbDuo" 153 | apply: 154 | name: bulbduo 155 | build_vars: 156 | FS_SIZE: 262144 157 | FLASH_SIZE: 2097152 158 | BOOT_CONFIG_ADDR: 0x7000 159 | MGOS_ROOT_FS_TYPE: SPIFFS 160 | - when: build_vars.MODEL == "ShellyI3" 161 | apply: 162 | name: ix3 163 | build_vars: 164 | FS_SIZE: 262144 165 | FLASH_SIZE: 2097152 166 | BOOT_CONFIG_ADDR: 0x1000 167 | MGOS_ROOT_FS_TYPE: SPIFFS 168 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "mgos.h" 2 | #include "spi_flash.h" 3 | #include "esp_rboot.h" 4 | 5 | #define BLOCK_SIZE 4096 6 | #define CHUNK_SIZE 4096 7 | #define TEMP_STORAGE 0 8 | 9 | // current bootloader configuration 10 | rboot_config *rboot_cfg; 11 | 12 | /* 13 | TODO 14 | * hash verify download! 15 | * if failed wait 60 sec and reboot 16 | * disable wdt and interrupts during critical write operations 17 | * move block for bootloader (0...4096) and its config (BOOT_CONFIG_ADDR ... BOOT_CONFIG_ADDR) last 18 | * move to esp flash write lib: 19 | #include "esp_flash_writer.h" 20 | static struct esp_flash_write_ctx s_wctx; 21 | ... 22 | case MG_EV_CONNECT: 23 | esp_init_flash_write_ctx(0x2000, (0x100000 - 0x2000)); 24 | ... 25 | case MG_EV_HTTP_CHUNK: 26 | esp_flash_write(&s_wctx, hm->body); 27 | */ 28 | 29 | /* 30 | src src address 31 | dest destination addess 32 | length block length / byte 33 | */ 34 | void block_copy(uint32 src, uint32 dest, uint32 length) { 35 | LOG(LL_DEBUG, ("block_copy start: cp %d bytes from 0x%x to 0x%x", length, src, dest)); 36 | uint32 chunk = CHUNK_SIZE, offset = 0; 37 | bool done = false; 38 | char *data = NULL; 39 | 40 | if (CHUNK_SIZE > BLOCK_SIZE || BLOCK_SIZE % CHUNK_SIZE != 0) { 41 | LOG(LL_ERROR, ("invalid CHUNK_SIZE. must be a divider of BLOCK_SIZE and < BLOCK_SIZE")); 42 | goto clean; 43 | } 44 | if ( src % BLOCK_SIZE != 0 || dest % BLOCK_SIZE != 0 ) { 45 | LOG(LL_ERROR, ("cannot copy: src or dest not aligned with fs block")); 46 | goto clean; 47 | } 48 | data = (char*) malloc(chunk); 49 | if (data == NULL) { 50 | LOG(LL_ERROR, ("out of memory")); 51 | goto clean; 52 | } 53 | 54 | while ( !done ) { 55 | if ( (long) (length - offset - chunk) <= 0 ) { 56 | chunk = length - offset; 57 | done = true; 58 | } 59 | // erase new sector 60 | if ( offset % BLOCK_SIZE == 0 ) { 61 | if ( spi_flash_erase_sector( (dest + offset) / BLOCK_SIZE ) != 0 ) { 62 | LOG(LL_ERROR, ("flash delete error! abort.")); 63 | goto clean; 64 | } 65 | } 66 | 67 | if ( spi_flash_read ( src + offset, (uint32 *) data, chunk) != 0 ) { 68 | LOG(LL_ERROR, ("flash read error! abort.")); 69 | goto clean; 70 | } 71 | if ( spi_flash_write( dest + offset, (uint32 *) data, chunk) != 0 ) { 72 | LOG(LL_ERROR, ("flash write error! abort.")); 73 | goto clean; 74 | } 75 | offset = offset + chunk; 76 | } 77 | LOG(LL_DEBUG, ("block_copy done")); 78 | 79 | clean: 80 | if (data != NULL) { free(data); } 81 | }; 82 | 83 | struct state { 84 | int status; // request status 85 | uint curr_blk, next_blk; // current / next block to be written 86 | uint left_in_block; // bytes left until current buffer is full 87 | uint32 recieved; // number of bytes recieved 88 | uint32 dest; // target flash address 89 | char data[BLOCK_SIZE]; // buffer for a block 90 | }; 91 | 92 | static void http_cb(struct mg_connection *c, int ev, void *ev_data, void *ud) { 93 | struct http_message *hm = (struct http_message *) ev_data; 94 | struct state *state = (struct state *) ud; 95 | 96 | switch (ev) { 97 | case MG_EV_CONNECT: 98 | // sent when a new outbound connection is created 99 | state->status = *(int *) ev_data; 100 | break; 101 | case MG_EV_HTTP_CHUNK: 102 | // esp8266 expects flash to be erased and written in blocks of 4kb, but webservers send 103 | // chunks of data in whatever size they please. we thus buffer the data and write it 104 | // whenever a block is full 105 | state->next_blk = ( state->dest + state->recieved + (uint32) hm->body.len ) / BLOCK_SIZE; 106 | state->left_in_block = ( (state->curr_blk + 1) * BLOCK_SIZE ) - state->dest - state->recieved; 107 | 108 | if ( state->next_blk > state->curr_blk ) { 109 | memcpy(&state->data[BLOCK_SIZE - state->left_in_block], hm->body.p, state->left_in_block); 110 | 111 | if ( spi_flash_erase_sector(state->curr_blk) != 0 ) { 112 | LOG(LL_ERROR, ("flash delete error! abort at %d recieved bytes.", state->recieved)); 113 | c->flags |= MG_F_CLOSE_IMMEDIATELY; 114 | state->status = 500; 115 | break; 116 | } 117 | if ( spi_flash_write( state->curr_blk * BLOCK_SIZE, (uint32 *) state->data, BLOCK_SIZE) != 0 ) { 118 | LOG(LL_ERROR, ("flash write error! abort at %d recieved bytes.", state->recieved)); 119 | c->flags |= MG_F_CLOSE_IMMEDIATELY; 120 | state->status = 500; 121 | break; 122 | } 123 | state->curr_blk = state->next_blk; 124 | 125 | memcpy(&state->data[0], hm->body.p + state->left_in_block, hm->body.len - state->left_in_block); 126 | } else { 127 | memcpy(&state->data[BLOCK_SIZE - state->left_in_block], hm->body.p, hm->body.len); 128 | } 129 | state->recieved += (uint32) hm->body.len; 130 | c->flags |= MG_F_DELETE_CHUNK; 131 | break; 132 | case MG_EV_HTTP_REPLY: 133 | // set status once file transfer is done 134 | state->status = hm->resp_code; 135 | c->flags |= MG_F_CLOSE_IMMEDIATELY; 136 | break; 137 | case MG_EV_CLOSE: 138 | // executed upon close connection 139 | LOG(LL_INFO, ("HTTP status is %d, recieved %d bytes", state->status, state->recieved)); 140 | if (state->status == 200) { 141 | // write last block 142 | if ( spi_flash_erase_sector(state->curr_blk) != 0 ) { 143 | LOG(LL_ERROR, ("flash delete error! abort at %d recieved bytes.", state->recieved)); 144 | state->status = 500; 145 | break; 146 | } 147 | state->left_in_block = ( (state->curr_blk + 1) * BLOCK_SIZE ) - state->dest - state->recieved; 148 | if ( spi_flash_write( state->curr_blk * BLOCK_SIZE, (uint32 *) state->data, BLOCK_SIZE - state->left_in_block) != 0 ) { 149 | LOG(LL_ERROR, ("flash write error! abort at %d recieved bytes.", state->recieved)); 150 | state->status = 500; 151 | break; 152 | } 153 | LOG(LL_DEBUG, ("last block dump done")); 154 | 155 | block_copy( (*rboot_cfg).roms[TEMP_STORAGE], 0, state->recieved ); //TODO move me 156 | mgos_system_restart_after(200); 157 | } else { 158 | LOG(LL_ERROR, ("HTTP state not 200, abort!")); 159 | } 160 | break; 161 | } 162 | }; 163 | 164 | /* 165 | url src url 166 | dest destination addess 167 | */ 168 | void download_file_to_flash(const char *url, uint32 dest) { 169 | struct state *state; 170 | if ((state = calloc(1, sizeof(*state))) == NULL) { 171 | LOG(LL_ERROR, ("out of memory")); 172 | return; 173 | } 174 | state->dest = dest; 175 | state->recieved = 0; 176 | state->curr_blk = dest / BLOCK_SIZE; 177 | 178 | LOG(LL_DEBUG, ("fetching %s to 0x%x", url, dest)); 179 | if (!mg_connect_http(mgos_get_mgr(), http_cb, state, url, NULL, NULL)) { 180 | free(state); 181 | LOG(LL_ERROR, ("malformed URL")); 182 | return; 183 | } 184 | 185 | // TODO wait on download to finish, then continue here. also, return status 186 | // free(state); 187 | return; 188 | }; 189 | 190 | // if we are online, lets download and flash tasmota 191 | static void online_cb(int ev, void *evd, void *arg) { 192 | if ( ev == MGOS_NET_EV_IP_ACQUIRED ) { 193 | LOG(LL_INFO, ("device is online, downloading tasmota")); 194 | download_file_to_flash(mgos_sys_config_get_mg2x_url(), (*rboot_cfg).roms[TEMP_STORAGE] ); 195 | } 196 | (void) evd; 197 | (void) arg; 198 | } 199 | 200 | enum mgos_app_init_result mgos_app_init(void) { 201 | // get current bootloader configuration 202 | rboot_cfg = get_rboot_config(); 203 | 204 | // if we are running from app0, move flash content to app1 (and fs0 -> fs1) and reboot 205 | if ( (*rboot_cfg).current_rom == 0 ) { 206 | LOG(LL_INFO, ("FW booted from app0, copy to app1 and reboot")); 207 | block_copy( (*rboot_cfg).roms[0], (*rboot_cfg).roms[1], (*rboot_cfg).roms_sizes[0] ); 208 | block_copy( (*rboot_cfg).fs_addresses[0], (*rboot_cfg).fs_addresses[1], (*rboot_cfg).fs_sizes[0] ); 209 | 210 | // update bootloader configuration 211 | (*rboot_cfg).current_rom = 1; 212 | (*rboot_cfg).previous_rom = 0; 213 | (*rboot_cfg).fw_updated = 1; 214 | (*rboot_cfg).is_first_boot = 1; 215 | 216 | rboot_set_config(rboot_cfg); 217 | 218 | mgos_system_restart_after(200); 219 | } else { 220 | LOG(LL_INFO, ("FW booted from app1, waiting for network connection")); 221 | mgos_event_add_group_handler(MGOS_EVENT_GRP_NET, online_cb, NULL); 222 | } 223 | return MGOS_APP_INIT_SUCCESS; 224 | } 225 | --------------------------------------------------------------------------------