|
| 1 | +Standalone mode |
| 2 | +=============== |
| 3 | + |
| 4 | +**Standalone / Hostless / On-The-Edge mode** means that the OAK camera isn't connected to a host computer. This can |
| 5 | +be achieved by first :ref:`flashing the bootloader <Flash bootloader>` and then :ref:`flashing the pipeline <Flash pipeline>` |
| 6 | +and assets (NN models) to the OAK's flash memory. |
| 7 | + |
| 8 | +Standalone mode is **only possible on OAKs that have on-board flash** memory, which are currently `OAK IOT <https://docs.luxonis.com/projects/hardware/en/latest/#iot-designs>`__ |
| 9 | +and `OAK POE <https://docs.luxonis.com/projects/hardware/en/latest/#poe-designs>`__ camera models. |
| 10 | + |
| 11 | +Converting a demo to standalone mode |
| 12 | +#################################### |
| 13 | + |
| 14 | +Since there won't be any communication between the host and the device, you first need to remove all |
| 15 | +:ref:`XLinkOut` and :ref:`XLinkIn` nodes. This means that the device will only communicate with the "outside world" |
| 16 | +via either SPI (:ref:`SPIOut`/:ref:`SPIIn`) or :ref:`Script` node (GPIO/UART or network protocols if you have |
| 17 | +OAK POE mode; HTTP/TCP/UDP...). |
| 18 | + |
| 19 | +Next thing you can also remove the host-side code, which usually looks something like this: |
| 20 | + |
| 21 | +.. code-block:: python |
| 22 | +
|
| 23 | + with dai.Device(pipeline) as device: |
| 24 | + videoQ = device.getOutputQueue("video") |
| 25 | + faceDetQ = device.getOutputQueue("face_det") |
| 26 | + nnQ = device.getOutputQueue("nn") |
| 27 | +
|
| 28 | + while True: |
| 29 | + frame = videoQ.get().getCvFrame() |
| 30 | + # ... |
| 31 | +
|
| 32 | +
|
| 33 | +After you remove all host-side code, you would only be left with the :ref:`Pipeline` definition (with nodes/links). |
| 34 | +Since device no longer communicates with the host, you need to "route" your program's output through either SPI |
| 35 | +or script node, as mentioned above. |
| 36 | + |
| 37 | +Flash bootloader |
| 38 | +################ |
| 39 | + |
| 40 | +Execute the code below to flash the :ref:`Bootloader` to the device. The bootloader is packaged together with the |
| 41 | +depthai, so if you have the latest depthai version, you will flash the latest bootloader version. This step |
| 42 | +is required only once. |
| 43 | + |
| 44 | +.. code-block:: python |
| 45 | +
|
| 46 | + import depthai as dai |
| 47 | + (f, bl) = dai.DeviceBootloader.getFirstAvailableDevice() |
| 48 | + bootloader = dai.DeviceBootloader(bl) |
| 49 | + progress = lambda p : print(f'Flashing progress: {p*100:.1f}%') |
| 50 | + bootloader.flashBootloader(progress) |
| 51 | +
|
| 52 | +Flash pipeline |
| 53 | +############## |
| 54 | + |
| 55 | +After you have standalone :ref:`Pipeline` definition and :ref:`Bootloader` already flashed on the device, you |
| 56 | +can start with flashing the pipeline. You can flash the pipeline with the following snippet: |
| 57 | + |
| 58 | +.. code-block:: python |
| 59 | +
|
| 60 | + import depthai as dai |
| 61 | +
|
| 62 | + pipeline = dai.Pipeline() |
| 63 | +
|
| 64 | + # Define standalone pipeline; add nodes and link them |
| 65 | + # cam = pipeline.create(dai.node.ColorCamera) |
| 66 | + # script = pipeline.create(dai.node.Script) |
| 67 | + # ... |
| 68 | +
|
| 69 | + # Flash the pipeline |
| 70 | + (f, bl) = dai.DeviceBootloader.getFirstAvailableDevice() |
| 71 | + bootloader = dai.DeviceBootloader(bl) |
| 72 | + progress = lambda p : print(f'Flashing progress: {p*100:.1f}%') |
| 73 | + bootloader.flash(progress, pipeline) |
| 74 | +
|
| 75 | +After successfully flashing the pipeline, it will get started automatically when you power up the device. |
| 76 | +If you would like to change the flashed pipeline, simply re-flash it again. |
| 77 | + |
| 78 | +Clear flash |
| 79 | +########### |
| 80 | + |
| 81 | +Since pipeline will start when powering the device, this can lead to unnecesary heating. If you would like to clear |
| 82 | +the flashed pipeline, use the code snippet below. |
| 83 | + |
| 84 | +.. warning:: |
| 85 | + Code below doesn't work yet. We will be adding "flashClear" helper function to the library. |
| 86 | + |
| 87 | +.. code-block:: python |
| 88 | +
|
| 89 | + import depthai as dai |
| 90 | + (f, bl) = dai.DeviceBootloader.getFirstAvailableDevice() |
| 91 | + if not f: |
| 92 | + print('No devices found, exiting...') |
| 93 | + exit(-1) |
| 94 | +
|
| 95 | + with dai.DeviceBootloader(bl) as bootloader: |
| 96 | + bootloader.flashClear() |
| 97 | +
|
| 98 | +Factory reset |
| 99 | +############# |
| 100 | + |
| 101 | +In case you have soft-bricked your device, or just want to clear everything (flashed pipeline/assets and bootloader config), |
| 102 | +we recommend running the factory reset script below. It will also flash the latest bootloader version. |
| 103 | + |
| 104 | +.. code-block:: python |
| 105 | +
|
| 106 | + import depthai as dai |
| 107 | + import tempfile |
| 108 | +
|
| 109 | + blBinary = dai.DeviceBootloader.getEmbeddedBootloaderBinary(dai.DeviceBootloader.Type.NETWORK) |
| 110 | + blBinary = blBinary + ([0xFF] * ((8 * 1024 * 1024 + 512) - len(blBinary))) |
| 111 | +
|
| 112 | + with tempfile.NamedTemporaryFile() as tmpBlFw: |
| 113 | + tmpBlFw.write(bytes(blBinary)) |
| 114 | +
|
| 115 | + (f, device_info) = dai.DeviceBootloader.getFirstAvailableDevice() |
| 116 | + if not f: |
| 117 | + print('No devices found, exiting...') |
| 118 | + exit(-1) |
| 119 | +
|
| 120 | + with dai.DeviceBootloader(device_info, allowFlashingBootloader=True) as bootloader: |
| 121 | + progress = lambda p : print(f'Factory reset progress: {p*100:.1f}%') |
| 122 | + # Override SBR table, to prevent booting flashed application |
| 123 | + [success, msg] = bootloader.flashBootloader(progress, tmpBlFw.name) |
| 124 | + if success: |
| 125 | + print('Successfully overwritten SBR table. Device should now be reacheable through PoE') |
| 126 | + else: |
| 127 | + print(f"Couldn't overwrite SBR table to unbrick the device. Error: {msg}") |
| 128 | +
|
| 129 | +You can also **factory reset OAK POE at specific IP** if it's not reachable (not in same LAN). |
| 130 | + |
| 131 | +.. code-block:: python |
| 132 | +
|
| 133 | + import depthai as dai |
| 134 | + import tempfile |
| 135 | +
|
| 136 | + blBinary = dai.DeviceBootloader.getEmbeddedBootloaderBinary(dai.DeviceBootloader.Type.NETWORK) |
| 137 | + blBinary = blBinary + ([0xFF] * ((8 * 1024 * 1024 + 512) - len(blBinary))) |
| 138 | +
|
| 139 | + with tempfile.NamedTemporaryFile() as tmpBlFw: |
| 140 | + tmpBlFw.write(bytes(blBinary)) |
| 141 | +
|
| 142 | + device_info = dai.DeviceInfo() |
| 143 | + device_info.state = dai.XLinkDeviceState.X_LINK_BOOTLOADER |
| 144 | + device_info.desc.protocol = dai.XLinkProtocol.X_LINK_TCP_IP |
| 145 | + device_info.desc.name = "192.168.34.110" # Set IP here |
| 146 | +
|
| 147 | + with dai.DeviceBootloader(device_info, allowFlashingBootloader=True) as bootloader: |
| 148 | + progress = lambda p : print(f'Factory reset progress: {p*100:.1f}%') |
| 149 | + # Override SBR table, to prevent booting flashed application |
| 150 | + [success, msg] = bootloader.flashBootloader(progress, tmpBlFw.name) |
| 151 | + if success: |
| 152 | + print('Successfully overwritten SBR table. Device should now be reacheable through PoE') |
| 153 | + else: |
| 154 | + print(f"Couldn't overwrite SBR table to unbrick the device. Error: {msg}") |
| 155 | +
|
| 156 | +
|
| 157 | +.. include:: ../includes/footer-short.rst |
0 commit comments