[{"data":1,"prerenderedAt":1182},["ShallowReactive",2],{"docs-\u002Fdocs\u002Fhow-to\u002Fflash-ota":3},{"id":4,"title":5,"body":6,"description":1175,"extension":1176,"meta":1177,"navigation":137,"path":1178,"seo":1179,"stem":1180,"__hash__":1181},"content\u002Fdocs\u002Fhow-to\u002Fflash-ota.md","Update Firmware Over the Air",{"type":7,"value":8,"toc":1163},"minimark",[9,13,26,29,56,63,68,74,121,198,225,229,232,237,532,536,652,656,806,810,832,930,934,1017,1021,1102,1108,1112,1159],[10,11,5],"h1",{"id":12},"update-firmware-over-the-air",[14,15,16,17,21,22,25],"p",{},"Once a device is running CONDUYT firmware compiled with ",[18,19,20],"code",{},"-DCONDUYT_OTA",", the host can replace its firmware over whatever transport is already connected (Serial, BLE, MQTT bridge, WebSerial in the browser). No USB cable, no DFU button, no ",[18,23,24],{},"esp-web-tools",".",[14,27,28],{},"The flow is simple:",[30,31,32,36,43,50],"ol",{},[33,34,35],"li",{},"The host computes a SHA-256 of the new firmware blob.",[33,37,38,39,42],{},"It sends ",[18,40,41],{},"OTA_BEGIN"," with the total byte count + the SHA-256.",[33,44,45,46,49],{},"It streams the firmware in ",[18,47,48],{},"OTA_CHUNK"," packets at sequential offsets.",[33,51,38,52,55],{},[18,53,54],{},"OTA_FINALIZE"," to verify the digest and reboot into the new image.",[14,57,58,59,62],{},"Every SDK ships a typed ",[18,60,61],{},"ConduytOTA"," orchestrator that does all four steps for you.",[64,65,67],"h2",{"id":66},"firmware-setup","Firmware setup",[14,69,70,71,73],{},"OTA support is opt-in per board. Add the compile flag and let CONDUYT's ",[18,72,61],{}," runtime hook into the device:",[75,76,81],"pre",{"className":77,"code":78,"language":79,"meta":80,"style":80},"language-ini shiki shiki-themes github-light github-dark","; platformio.ini\n[env:esp32dev]\nplatform = espressif32\nboard = esp32dev\nframework = arduino\nbuild_flags = -DCONDUYT_OTA\n","ini","",[18,82,83,91,97,103,109,115],{"__ignoreMap":80},[84,85,88],"span",{"class":86,"line":87},"line",1,[84,89,90],{},"; platformio.ini\n",[84,92,94],{"class":86,"line":93},2,[84,95,96],{},"[env:esp32dev]\n",[84,98,100],{"class":86,"line":99},3,[84,101,102],{},"platform = espressif32\n",[84,104,106],{"class":86,"line":105},4,[84,107,108],{},"board = esp32dev\n",[84,110,112],{"class":86,"line":111},5,[84,113,114],{},"framework = arduino\n",[84,116,118],{"class":86,"line":117},6,[84,119,120],{},"build_flags = -DCONDUYT_OTA\n",[75,122,126],{"className":123,"code":124,"language":125,"meta":80,"style":80},"language-cpp shiki shiki-themes github-light github-dark","#include \u003CConduyt.h>\n\nConduytSerial transport(Serial, 115200);\nConduytDevice device(\"MyBoard\", \"1.0.0\", transport);\n\nvoid setup() {\n  Serial.begin(115200);\n  device.begin();   \u002F\u002F OTA handlers wire in automatically when CONDUYT_OTA is defined\n}\n\nvoid loop() {\n  device.poll();\n}\n","cpp",[18,127,128,133,139,144,149,153,158,164,170,176,181,187,193],{"__ignoreMap":80},[84,129,130],{"class":86,"line":87},[84,131,132],{},"#include \u003CConduyt.h>\n",[84,134,135],{"class":86,"line":93},[84,136,138],{"emptyLinePlaceholder":137},true,"\n",[84,140,141],{"class":86,"line":99},[84,142,143],{},"ConduytSerial transport(Serial, 115200);\n",[84,145,146],{"class":86,"line":105},[84,147,148],{},"ConduytDevice device(\"MyBoard\", \"1.0.0\", transport);\n",[84,150,151],{"class":86,"line":111},[84,152,138],{"emptyLinePlaceholder":137},[84,154,155],{"class":86,"line":117},[84,156,157],{},"void setup() {\n",[84,159,161],{"class":86,"line":160},7,[84,162,163],{},"  Serial.begin(115200);\n",[84,165,167],{"class":86,"line":166},8,[84,168,169],{},"  device.begin();   \u002F\u002F OTA handlers wire in automatically when CONDUYT_OTA is defined\n",[84,171,173],{"class":86,"line":172},9,[84,174,175],{},"}\n",[84,177,179],{"class":86,"line":178},10,[84,180,138],{"emptyLinePlaceholder":137},[84,182,184],{"class":86,"line":183},11,[84,185,186],{},"void loop() {\n",[84,188,190],{"class":86,"line":189},12,[84,191,192],{},"  device.poll();\n",[84,194,196],{"class":86,"line":195},13,[84,197,175],{},[14,199,200,201,205,206,209,210,213,214,216,217,220,221,224],{},"OTA today is supported on the ",[202,203,204],"strong",{},"ESP32 family"," (esp32, S2, S3, C3) and ",[202,207,208],{},"Arduino Nano ESP32"," — wherever ",[18,211,212],{},"Update.h"," is available. AVR boards (Uno R3, Nano, Mega) and most Cortex-M boards (Teensy, Pico, R4 Minima) lack the bootloader \u002F off-chip flash needed for in-place updates and currently NAK ",[18,215,41],{}," with ",[18,218,219],{},"OTA_INVALID"," (0x0F). Check ",[18,222,223],{},"device.capabilities.otaCapable"," before starting.",[64,226,228],{"id":227},"host-usage","Host usage",[14,230,231],{},"Identical wire bytes, identical chunk size auto-tuning, identical progress callback, in every SDK:",[233,234,236],"h3",{"id":235},"javascript","JavaScript",[75,238,241],{"className":239,"code":240,"language":235,"meta":80,"style":80},"language-javascript shiki shiki-themes github-light github-dark","import { ConduytDevice, ConduytOTA } from 'conduyt-js'\nimport { SerialTransport } from 'conduyt-js\u002Ftransports\u002Fserial'\n\nconst device = await ConduytDevice.connect(new SerialTransport({ path: '\u003CYOUR_PORT>' }))\nif (!device.capabilities?.otaCapable) {\n  throw new Error('Firmware does not support OTA — rebuild with -DCONDUYT_OTA')\n}\n\nconst fw = new Uint8Array(await fs.promises.readFile('.\u002Ffirmware.bin'))\nconst ota = new ConduytOTA(device)\n\nawait ota.flash(fw, {\n  onProgress: (sent, total) => console.log(`${sent}\u002F${total} (${(100 * sent \u002F total).toFixed(1)}%)`),\n})\n\nconsole.log('Flashed — device is rebooting.')\n",[18,242,243,260,272,276,316,330,349,353,357,390,407,411,424,506,512,517],{"__ignoreMap":80},[84,244,245,249,253,256],{"class":86,"line":87},[84,246,248],{"class":247},"szBVR","import",[84,250,252],{"class":251},"sVt8B"," { ConduytDevice, ConduytOTA } ",[84,254,255],{"class":247},"from",[84,257,259],{"class":258},"sZZnC"," 'conduyt-js'\n",[84,261,262,264,267,269],{"class":86,"line":93},[84,263,248],{"class":247},[84,265,266],{"class":251}," { SerialTransport } ",[84,268,255],{"class":247},[84,270,271],{"class":258}," 'conduyt-js\u002Ftransports\u002Fserial'\n",[84,273,274],{"class":86,"line":99},[84,275,138],{"emptyLinePlaceholder":137},[84,277,278,281,285,288,291,294,298,301,304,307,310,313],{"class":86,"line":105},[84,279,280],{"class":247},"const",[84,282,284],{"class":283},"sj4cs"," device",[84,286,287],{"class":247}," =",[84,289,290],{"class":247}," await",[84,292,293],{"class":251}," ConduytDevice.",[84,295,297],{"class":296},"sScJk","connect",[84,299,300],{"class":251},"(",[84,302,303],{"class":247},"new",[84,305,306],{"class":296}," SerialTransport",[84,308,309],{"class":251},"({ path: ",[84,311,312],{"class":258},"'\u003CYOUR_PORT>'",[84,314,315],{"class":251}," }))\n",[84,317,318,321,324,327],{"class":86,"line":111},[84,319,320],{"class":247},"if",[84,322,323],{"class":251}," (",[84,325,326],{"class":247},"!",[84,328,329],{"class":251},"device.capabilities?.otaCapable) {\n",[84,331,332,335,338,341,343,346],{"class":86,"line":117},[84,333,334],{"class":247},"  throw",[84,336,337],{"class":247}," new",[84,339,340],{"class":296}," Error",[84,342,300],{"class":251},[84,344,345],{"class":258},"'Firmware does not support OTA — rebuild with -DCONDUYT_OTA'",[84,347,348],{"class":251},")\n",[84,350,351],{"class":86,"line":160},[84,352,175],{"class":251},[84,354,355],{"class":86,"line":166},[84,356,138],{"emptyLinePlaceholder":137},[84,358,359,361,364,366,368,371,373,376,379,382,384,387],{"class":86,"line":172},[84,360,280],{"class":247},[84,362,363],{"class":283}," fw",[84,365,287],{"class":247},[84,367,337],{"class":247},[84,369,370],{"class":296}," Uint8Array",[84,372,300],{"class":251},[84,374,375],{"class":247},"await",[84,377,378],{"class":251}," fs.promises.",[84,380,381],{"class":296},"readFile",[84,383,300],{"class":251},[84,385,386],{"class":258},"'.\u002Ffirmware.bin'",[84,388,389],{"class":251},"))\n",[84,391,392,394,397,399,401,404],{"class":86,"line":178},[84,393,280],{"class":247},[84,395,396],{"class":283}," ota",[84,398,287],{"class":247},[84,400,337],{"class":247},[84,402,403],{"class":296}," ConduytOTA",[84,405,406],{"class":251},"(device)\n",[84,408,409],{"class":86,"line":183},[84,410,138],{"emptyLinePlaceholder":137},[84,412,413,415,418,421],{"class":86,"line":189},[84,414,375],{"class":247},[84,416,417],{"class":251}," ota.",[84,419,420],{"class":296},"flash",[84,422,423],{"class":251},"(fw, {\n",[84,425,426,429,432,436,439,442,445,448,451,454,456,459,461,464,466,469,471,474,477,480,483,486,489,492,494,497,500,503],{"class":86,"line":195},[84,427,428],{"class":296},"  onProgress",[84,430,431],{"class":251},": (",[84,433,435],{"class":434},"s4XuR","sent",[84,437,438],{"class":251},", ",[84,440,441],{"class":434},"total",[84,443,444],{"class":251},") ",[84,446,447],{"class":247},"=>",[84,449,450],{"class":251}," console.",[84,452,453],{"class":296},"log",[84,455,300],{"class":251},[84,457,458],{"class":258},"`${",[84,460,435],{"class":251},[84,462,463],{"class":258},"}\u002F${",[84,465,441],{"class":251},[84,467,468],{"class":258},"} (${",[84,470,300],{"class":258},[84,472,473],{"class":283},"100",[84,475,476],{"class":247}," *",[84,478,479],{"class":251}," sent",[84,481,482],{"class":247}," \u002F",[84,484,485],{"class":251}," total",[84,487,488],{"class":258},").",[84,490,491],{"class":296},"toFixed",[84,493,300],{"class":258},[84,495,496],{"class":283},"1",[84,498,499],{"class":258},")",[84,501,502],{"class":258},"}%)`",[84,504,505],{"class":251},"),\n",[84,507,509],{"class":86,"line":508},14,[84,510,511],{"class":251},"})\n",[84,513,515],{"class":86,"line":514},15,[84,516,138],{"emptyLinePlaceholder":137},[84,518,520,523,525,527,530],{"class":86,"line":519},16,[84,521,522],{"class":251},"console.",[84,524,453],{"class":296},[84,526,300],{"class":251},[84,528,529],{"class":258},"'Flashed — device is rebooting.'",[84,531,348],{"class":251},[233,533,535],{"id":534},"python","Python",[75,537,540],{"className":538,"code":539,"language":534,"meta":80,"style":80},"language-python shiki shiki-themes github-light github-dark","import asyncio\nfrom conduyt import ConduytDevice, ConduytOTA\nfrom conduyt.transports.serial import SerialTransport\n\n\nasync def main():\n    device = ConduytDevice(SerialTransport('\u003CYOUR_PORT>'))\n    await device.connect()\n    if not device.capabilities.ota_capable:\n        raise RuntimeError(\"Firmware does not support OTA — rebuild with -DCONDUYT_OTA\")\n\n    with open('firmware.bin', 'rb') as f:\n        fw = f.read()\n\n    ota = ConduytOTA(device)\n    await ota.flash(\n        fw,\n        on_progress=lambda sent, total: print(f\"{sent}\u002F{total} ({100*sent\u002Ftotal:.1f}%)\"),\n    )\n\n\nasyncio.run(main())\n",[18,541,542,547,552,557,561,565,570,575,580,585,590,594,599,604,608,613,618,624,630,636,641,646],{"__ignoreMap":80},[84,543,544],{"class":86,"line":87},[84,545,546],{},"import asyncio\n",[84,548,549],{"class":86,"line":93},[84,550,551],{},"from conduyt import ConduytDevice, ConduytOTA\n",[84,553,554],{"class":86,"line":99},[84,555,556],{},"from conduyt.transports.serial import SerialTransport\n",[84,558,559],{"class":86,"line":105},[84,560,138],{"emptyLinePlaceholder":137},[84,562,563],{"class":86,"line":111},[84,564,138],{"emptyLinePlaceholder":137},[84,566,567],{"class":86,"line":117},[84,568,569],{},"async def main():\n",[84,571,572],{"class":86,"line":160},[84,573,574],{},"    device = ConduytDevice(SerialTransport('\u003CYOUR_PORT>'))\n",[84,576,577],{"class":86,"line":166},[84,578,579],{},"    await device.connect()\n",[84,581,582],{"class":86,"line":172},[84,583,584],{},"    if not device.capabilities.ota_capable:\n",[84,586,587],{"class":86,"line":178},[84,588,589],{},"        raise RuntimeError(\"Firmware does not support OTA — rebuild with -DCONDUYT_OTA\")\n",[84,591,592],{"class":86,"line":183},[84,593,138],{"emptyLinePlaceholder":137},[84,595,596],{"class":86,"line":189},[84,597,598],{},"    with open('firmware.bin', 'rb') as f:\n",[84,600,601],{"class":86,"line":195},[84,602,603],{},"        fw = f.read()\n",[84,605,606],{"class":86,"line":508},[84,607,138],{"emptyLinePlaceholder":137},[84,609,610],{"class":86,"line":514},[84,611,612],{},"    ota = ConduytOTA(device)\n",[84,614,615],{"class":86,"line":519},[84,616,617],{},"    await ota.flash(\n",[84,619,621],{"class":86,"line":620},17,[84,622,623],{},"        fw,\n",[84,625,627],{"class":86,"line":626},18,[84,628,629],{},"        on_progress=lambda sent, total: print(f\"{sent}\u002F{total} ({100*sent\u002Ftotal:.1f}%)\"),\n",[84,631,633],{"class":86,"line":632},19,[84,634,635],{},"    )\n",[84,637,639],{"class":86,"line":638},20,[84,640,138],{"emptyLinePlaceholder":137},[84,642,644],{"class":86,"line":643},21,[84,645,138],{"emptyLinePlaceholder":137},[84,647,649],{"class":86,"line":648},22,[84,650,651],{},"asyncio.run(main())\n",[233,653,655],{"id":654},"go","Go",[75,657,660],{"className":658,"code":659,"language":654,"meta":80,"style":80},"language-go shiki shiki-themes github-light github-dark","package main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"log\"\n    \"os\"\n\n    conduyt \"github.com\u002Fvirgilvox\u002Fconduyt\u002Fsdk\u002Fgo\"\n    \"github.com\u002Fvirgilvox\u002Fconduyt\u002Fsdk\u002Fgo\u002Fota\"\n    \"github.com\u002Fvirgilvox\u002Fconduyt\u002Fsdk\u002Fgo\u002Ftransports\"\n)\n\nfunc main() {\n    transport, _ := transports.NewSerial(\"\u002Fdev\u002Fcu.usbmodem14101\", 115200)\n    device := conduyt.NewDevice(transport, 5_000_000_000)\n    ctx := context.Background()\n    if _, err := device.Connect(ctx); err != nil { log.Fatal(err) }\n\n    fw, err := os.ReadFile(\"firmware.bin\")\n    if err != nil { log.Fatal(err) }\n\n    err = ota.Flash(ctx, device, fw, ota.Options{\n        OnProgress: func(sent, total int) {\n            fmt.Printf(\"%d \u002F %d (%.1f%%)\\n\", sent, total, 100.0*float64(sent)\u002Ffloat64(total))\n        },\n    })\n    if err != nil { log.Fatal(err) }\n}\n",[18,661,662,667,671,676,681,686,691,696,700,705,710,715,719,723,728,733,738,743,748,752,757,762,766,772,778,784,790,796,801],{"__ignoreMap":80},[84,663,664],{"class":86,"line":87},[84,665,666],{},"package main\n",[84,668,669],{"class":86,"line":93},[84,670,138],{"emptyLinePlaceholder":137},[84,672,673],{"class":86,"line":99},[84,674,675],{},"import (\n",[84,677,678],{"class":86,"line":105},[84,679,680],{},"    \"context\"\n",[84,682,683],{"class":86,"line":111},[84,684,685],{},"    \"fmt\"\n",[84,687,688],{"class":86,"line":117},[84,689,690],{},"    \"log\"\n",[84,692,693],{"class":86,"line":160},[84,694,695],{},"    \"os\"\n",[84,697,698],{"class":86,"line":166},[84,699,138],{"emptyLinePlaceholder":137},[84,701,702],{"class":86,"line":172},[84,703,704],{},"    conduyt \"github.com\u002Fvirgilvox\u002Fconduyt\u002Fsdk\u002Fgo\"\n",[84,706,707],{"class":86,"line":178},[84,708,709],{},"    \"github.com\u002Fvirgilvox\u002Fconduyt\u002Fsdk\u002Fgo\u002Fota\"\n",[84,711,712],{"class":86,"line":183},[84,713,714],{},"    \"github.com\u002Fvirgilvox\u002Fconduyt\u002Fsdk\u002Fgo\u002Ftransports\"\n",[84,716,717],{"class":86,"line":189},[84,718,348],{},[84,720,721],{"class":86,"line":195},[84,722,138],{"emptyLinePlaceholder":137},[84,724,725],{"class":86,"line":508},[84,726,727],{},"func main() {\n",[84,729,730],{"class":86,"line":514},[84,731,732],{},"    transport, _ := transports.NewSerial(\"\u002Fdev\u002Fcu.usbmodem14101\", 115200)\n",[84,734,735],{"class":86,"line":519},[84,736,737],{},"    device := conduyt.NewDevice(transport, 5_000_000_000)\n",[84,739,740],{"class":86,"line":620},[84,741,742],{},"    ctx := context.Background()\n",[84,744,745],{"class":86,"line":626},[84,746,747],{},"    if _, err := device.Connect(ctx); err != nil { log.Fatal(err) }\n",[84,749,750],{"class":86,"line":632},[84,751,138],{"emptyLinePlaceholder":137},[84,753,754],{"class":86,"line":638},[84,755,756],{},"    fw, err := os.ReadFile(\"firmware.bin\")\n",[84,758,759],{"class":86,"line":643},[84,760,761],{},"    if err != nil { log.Fatal(err) }\n",[84,763,764],{"class":86,"line":648},[84,765,138],{"emptyLinePlaceholder":137},[84,767,769],{"class":86,"line":768},23,[84,770,771],{},"    err = ota.Flash(ctx, device, fw, ota.Options{\n",[84,773,775],{"class":86,"line":774},24,[84,776,777],{},"        OnProgress: func(sent, total int) {\n",[84,779,781],{"class":86,"line":780},25,[84,782,783],{},"            fmt.Printf(\"%d \u002F %d (%.1f%%)\\n\", sent, total, 100.0*float64(sent)\u002Ffloat64(total))\n",[84,785,787],{"class":86,"line":786},26,[84,788,789],{},"        },\n",[84,791,793],{"class":86,"line":792},27,[84,794,795],{},"    })\n",[84,797,799],{"class":86,"line":798},28,[84,800,761],{},[84,802,804],{"class":86,"line":803},29,[84,805,175],{},[233,807,809],{"id":808},"rust","Rust",[75,811,815],{"className":812,"code":813,"language":814,"meta":80,"style":80},"language-toml shiki shiki-themes github-light github-dark","# Cargo.toml\n[dependencies]\nconduyt = { version = \"0.3\", features = [\"ota\"] }\n","toml",[18,816,817,822,827],{"__ignoreMap":80},[84,818,819],{"class":86,"line":87},[84,820,821],{},"# Cargo.toml\n",[84,823,824],{"class":86,"line":93},[84,825,826],{},"[dependencies]\n",[84,828,829],{"class":86,"line":99},[84,830,831],{},"conduyt = { version = \"0.3\", features = [\"ota\"] }\n",[75,833,836],{"className":834,"code":835,"language":808,"meta":80,"style":80},"language-rust shiki shiki-themes github-light github-dark","use conduyt::device::Device;\nuse conduyt::ota::{flash, FlashOptions};\nuse conduyt::transports::serial::SerialTransport;\n\nfn main() -> Result\u003C(), Box\u003Cdyn std::error::Error>> {\n    let transport = SerialTransport::open(\"\u002Fdev\u002Fcu.usbmodem14101\", 115200)?;\n    let mut device = Device::new(transport);\n    device.connect()?;\n\n    let fw = std::fs::read(\"firmware.bin\")?;\n    let mut on_progress = |sent: usize, total: usize| {\n        println!(\"{} \u002F {} ({:.1}%)\", sent, total, 100.0 * sent as f64 \u002F total as f64);\n    };\n    flash(&mut device, &fw, FlashOptions {\n        on_progress: Some(&mut on_progress),\n        ..Default::default()\n    })?;\n    Ok(())\n}\n",[18,837,838,843,848,853,857,862,867,872,877,881,886,891,896,901,906,911,916,921,926],{"__ignoreMap":80},[84,839,840],{"class":86,"line":87},[84,841,842],{},"use conduyt::device::Device;\n",[84,844,845],{"class":86,"line":93},[84,846,847],{},"use conduyt::ota::{flash, FlashOptions};\n",[84,849,850],{"class":86,"line":99},[84,851,852],{},"use conduyt::transports::serial::SerialTransport;\n",[84,854,855],{"class":86,"line":105},[84,856,138],{"emptyLinePlaceholder":137},[84,858,859],{"class":86,"line":111},[84,860,861],{},"fn main() -> Result\u003C(), Box\u003Cdyn std::error::Error>> {\n",[84,863,864],{"class":86,"line":117},[84,865,866],{},"    let transport = SerialTransport::open(\"\u002Fdev\u002Fcu.usbmodem14101\", 115200)?;\n",[84,868,869],{"class":86,"line":160},[84,870,871],{},"    let mut device = Device::new(transport);\n",[84,873,874],{"class":86,"line":166},[84,875,876],{},"    device.connect()?;\n",[84,878,879],{"class":86,"line":172},[84,880,138],{"emptyLinePlaceholder":137},[84,882,883],{"class":86,"line":178},[84,884,885],{},"    let fw = std::fs::read(\"firmware.bin\")?;\n",[84,887,888],{"class":86,"line":183},[84,889,890],{},"    let mut on_progress = |sent: usize, total: usize| {\n",[84,892,893],{"class":86,"line":189},[84,894,895],{},"        println!(\"{} \u002F {} ({:.1}%)\", sent, total, 100.0 * sent as f64 \u002F total as f64);\n",[84,897,898],{"class":86,"line":195},[84,899,900],{},"    };\n",[84,902,903],{"class":86,"line":508},[84,904,905],{},"    flash(&mut device, &fw, FlashOptions {\n",[84,907,908],{"class":86,"line":514},[84,909,910],{},"        on_progress: Some(&mut on_progress),\n",[84,912,913],{"class":86,"line":519},[84,914,915],{},"        ..Default::default()\n",[84,917,918],{"class":86,"line":620},[84,919,920],{},"    })?;\n",[84,922,923],{"class":86,"line":626},[84,924,925],{},"    Ok(())\n",[84,927,928],{"class":86,"line":632},[84,929,175],{},[233,931,933],{"id":932},"swift","Swift",[75,935,938],{"className":936,"code":937,"language":932,"meta":80,"style":80},"language-swift shiki shiki-themes github-light github-dark","import ConduytKit\nimport Foundation\n\n\u002F\u002F BLETransport's init takes optional `name:` or `uuid:` filters; bare-init\n\u002F\u002F connects to the first CONDUYT-service advertiser.\nlet transport = BLETransport()\nlet device = ConduytDevice(transport: transport)\n_ = try await device.connect()\n\nlet fwURL = Bundle.main.url(forResource: \"firmware\", withExtension: \"bin\")!\nlet fw = try Data(contentsOf: fwURL)\n\nlet ota = ConduytOTA(device: device)\ntry await ota.flash(fw, options: OTAFlashOptions(onProgress: { sent, total in\n    print(\"\\(sent) \u002F \\(total) (\\(100 * Double(sent) \u002F Double(total))%)\")\n}))\n",[18,939,940,945,950,954,959,964,969,974,979,983,988,993,997,1002,1007,1012],{"__ignoreMap":80},[84,941,942],{"class":86,"line":87},[84,943,944],{},"import ConduytKit\n",[84,946,947],{"class":86,"line":93},[84,948,949],{},"import Foundation\n",[84,951,952],{"class":86,"line":99},[84,953,138],{"emptyLinePlaceholder":137},[84,955,956],{"class":86,"line":105},[84,957,958],{},"\u002F\u002F BLETransport's init takes optional `name:` or `uuid:` filters; bare-init\n",[84,960,961],{"class":86,"line":111},[84,962,963],{},"\u002F\u002F connects to the first CONDUYT-service advertiser.\n",[84,965,966],{"class":86,"line":117},[84,967,968],{},"let transport = BLETransport()\n",[84,970,971],{"class":86,"line":160},[84,972,973],{},"let device = ConduytDevice(transport: transport)\n",[84,975,976],{"class":86,"line":166},[84,977,978],{},"_ = try await device.connect()\n",[84,980,981],{"class":86,"line":172},[84,982,138],{"emptyLinePlaceholder":137},[84,984,985],{"class":86,"line":178},[84,986,987],{},"let fwURL = Bundle.main.url(forResource: \"firmware\", withExtension: \"bin\")!\n",[84,989,990],{"class":86,"line":183},[84,991,992],{},"let fw = try Data(contentsOf: fwURL)\n",[84,994,995],{"class":86,"line":189},[84,996,138],{"emptyLinePlaceholder":137},[84,998,999],{"class":86,"line":195},[84,1000,1001],{},"let ota = ConduytOTA(device: device)\n",[84,1003,1004],{"class":86,"line":508},[84,1005,1006],{},"try await ota.flash(fw, options: OTAFlashOptions(onProgress: { sent, total in\n",[84,1008,1009],{"class":86,"line":514},[84,1010,1011],{},"    print(\"\\(sent) \u002F \\(total) (\\(100 * Double(sent) \u002F Double(total))%)\")\n",[84,1013,1014],{"class":86,"line":519},[84,1015,1016],{},"}))\n",[64,1018,1020],{"id":1019},"wire-format-reference","Wire format reference",[1022,1023,1024,1043],"table",{},[1025,1026,1027],"thead",{},[1028,1029,1030,1034,1037,1040],"tr",{},[1031,1032,1033],"th",{},"Command",[1031,1035,1036],{},"ID",[1031,1038,1039],{},"Payload",[1031,1041,1042],{},"NAK code on error",[1044,1045,1046,1066,1085],"tbody",{},[1028,1047,1048,1051,1056,1061],{},[1049,1050,41],"td",{},[1049,1052,1053],{},[18,1054,1055],{},"0x70",[1049,1057,1058],{},[18,1059,1060],{},"total_bytes (u32 LE) + sha256 (32 bytes)",[1049,1062,1063],{},[18,1064,1065],{},"0x0F OTA_INVALID",[1028,1067,1068,1070,1075,1080],{},[1049,1069,48],{},[1049,1071,1072],{},[18,1073,1074],{},"0x71",[1049,1076,1077],{},[18,1078,1079],{},"offset (u32 LE) + data (N bytes)",[1049,1081,1082,1084],{},[18,1083,1065],{}," (out-of-order or after Update.write fails)",[1028,1086,1087,1089,1094,1097],{},[1049,1088,54],{},[1049,1090,1091],{},[18,1092,1093],{},"0x72",[1049,1095,1096],{},"(empty)",[1049,1098,1099,1101],{},[18,1100,1065],{}," (digest mismatch \u002F Update.end failure)",[14,1103,1104,1105,1107],{},"After ",[18,1106,54],{}," ACKs, the firmware reboots immediately. Any packet sent on the same connection in the next ~1–3 seconds will fail. Reconnect.",[64,1109,1111],{"id":1110},"notes","Notes",[1113,1114,1115,1137,1147,1153],"ul",{},[33,1116,1117,1120,1121,1124,1125,1128,1129,1132,1133,1136],{},[202,1118,1119],{},"Chunk size",": each SDK auto-sizes chunks based on ",[18,1122,1123],{},"HELLO_RESP.maxPayload",". The device reserves 4 bytes of every payload for the offset header, so chunks are ",[18,1126,1127],{},"(maxPayload - 4)"," bytes by default. ESP32, RP2040, nRF52, STM32, and Teensy advertise 512 bytes (so chunks are 508), SAMD advertises 256, ATmega328 advertises 128. Override at firmware build time with ",[18,1130,1131],{},"#define CONDUYT_PACKET_BUF_SIZE 1024"," before ",[18,1134,1135],{},"#include \u003CConduyt.h>"," if you want larger chunks on capable boards.",[33,1138,1139,1142,1143,1146],{},[202,1140,1141],{},"Sequential offsets",": chunks must be sent in ascending offset order. The firmware NAKs any out-of-order chunk to keep ",[18,1144,1145],{},"Update.write"," happy. None of the SDK orchestrators reorder, so this is automatic if you use them.",[33,1148,1149,1152],{},[202,1150,1151],{},"Hashing on the host",": the SHA-256 is computed on the host and verified by the firmware at finalize time. A mid-flight transport corruption that slips past CRC8 will still get caught here.",[33,1154,1155,1158],{},[202,1156,1157],{},"Rollback",": there's no rollback in the OTA spec itself — if the new firmware bricks itself, you fall back to USB \u002F DFU. Keep a known-good build handy.",[1160,1161,1162],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":80,"searchDepth":93,"depth":93,"links":1164},[1165,1166,1173,1174],{"id":66,"depth":93,"text":67},{"id":227,"depth":93,"text":228,"children":1167},[1168,1169,1170,1171,1172],{"id":235,"depth":99,"text":236},{"id":534,"depth":99,"text":535},{"id":654,"depth":99,"text":655},{"id":808,"depth":99,"text":809},{"id":932,"depth":99,"text":933},{"id":1019,"depth":93,"text":1020},{"id":1110,"depth":93,"text":1111},"Flash new firmware over the existing CONDUYT connection — no USB cable, no DFU button.","md",{},"\u002Fdocs\u002Fhow-to\u002Fflash-ota",{"title":5,"description":1175},"docs\u002Fhow-to\u002Fflash-ota","gA_avrwIcl7OYqozY_6XDg8bi9aQbYR1Kxhl8mI7evA",1777412315481]