• DepthAI
  • How to assign static IPs to POE devices

Hi @erik,

I flashed the POE device with the new config.json bootloader to set a static IP, and power cycled the POE Camera but do not see the device via ping or dai.getAllAvailableDevices().

When I try to ping the new IP 172.16.15.201 ping halts and does not even say the host is unreachable. The old IP before flashing was assigned via DHCP as 172.16.15.70 so if I could reach the old IP, I don't see why I couldn't have reached the new one. Do you have any suggestions?

For reference I created a script below (thanks GitHub CoPilot) to create a new JSON file that was used to flash the device via "python3 bootloader_config.py flash new_config.json". bootloader_config.py said the flash was successful.

#!/usr/bin/env python3

# import module to read command line arguments
import argparse
import json
import re
import os

# read json file and return a dictionary
def read_json(json_file):
    with open(json_file) as json_data:
        d = json.load(json_data)
    return d

# convert ipv4 to 32 bit integer
def ipv4_to_int(ipv4):
    field1, field2, field3, field4 = ipv4.split(".")
    
    # same as int(field1)*(2**24) + int(field2)*(2**16) + int(field3)*(2**8) + int(field4)
    return int(field1) << 24 | int(field2) << 16 | int(field3) << 8 | int(field4)

# read file from command line and check if it exists and if it is a .json file
def read_file(file):
    if os.path.isfile(file):
        if file.endswith(".json"):
            return read_json(file)
        else:
            print(f"File {file} is not a .json file")
            exit(1)
    else:
        print(f"File {file} does not exist")
        exit(1)

# ask for arguments from command line
def get_args():
    parser = argparse.ArgumentParser(description="Generate a config file")
    parser.add_argument("-f", "--file", default="config.json", help="Path to the .json file")
    parser.add_argument("-o", "--output", help="Path to the output file")
    args = parser.parse_args()
    return args

# ask user for input
def get_input(question, check_ipv4=False, check_bool=False):
    user_input = input(question).strip()
    
    if check_ipv4:
        while not is_ipv4(user_input):
            print("Please enter a valid IPv4 address")
            user_input = input(question).strip()
    
    elif check_bool:
        while user_input not in ["y", "n"]:
            print("Please enter y or n")
            user_input = input(question).strip()
        
        return user_input == "y"
    
    return user_input

# check if string is ipv4
def is_ipv4(ipv4):
    return re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", ipv4)

def start_configuration():
    print("Started Bootloader IPv4 JSON Configuration")

    args = get_args()
    config = read_file(args.file)
    
    ipv4 = get_input("Enter the IPv4 Address: ", check_ipv4=True)
    static = get_input("Is this a static IPv4 Address? (y/n): ", check_bool=True)
    netmask = get_input("Enter the IPv4 Netmask: ", check_ipv4=True)
    gateway = get_input("Enter the IPv4 Gateway: ", check_ipv4=True)
    
    config["network"]["ipv4"] = ipv4_to_int(ipv4)
    config["network"]["staticIpv4"] = static
    config["network"]["ipv4Gateway"] = ipv4_to_int(gateway)
    config["network"]["ipv4Mask"] = ipv4_to_int(netmask)
    
    if args.output:
        with open(args.output, "w") as outfile:
            json.dump(config, outfile, indent=4)
        print(f"JSON Config Saved to {args.output}")
    else:
        print("JSON Config Result:")
        print(json.dumps(config, indent=4))

if __name__ == "__main__":
    start_configuration()

The resulting JSON created was below where ipv4 is 172.16.15.201, ipv4Mask is 255.255.255.0, ipv4Gateway is 172.16.15.254, and staticIpv4 was set to true:

{
    "appMem": -1,
    "network": {
        "ipv4": 2886733769,
        "ipv4Dns": 0,
        "ipv4DnsAlt": 0,
        "ipv4Gateway": 2886733822,
        "ipv4Mask": 4294967040,
        "ipv6": [
            0,
            0,
            0,
            0
        ],
        "ipv6Dns": [
            0,
            0,
            0,
            0
        ],
        "ipv6DnsAlt": [
            0,
            0,
            0,
            0
        ],
        "ipv6Gateway": [
            0,
            0,
            0,
            0
        ],
        "ipv6Prefix": 0,
        "mac": [
            0,
            0,
            0,
            0,
            0,
            0
        ],
        "staticIpv4": true,
        "staticIpv6": false,
        "timeoutMs": 30000
    },
    "usb": {
        "maxUsbSpeed": 3,
        "pid": 63036,
        "timeoutMs": 3000,
        "vid": 999
    }
}
  • erik replied to this.

    Hello gbanuru , I would actually suggest using the API itself (bootloader API), where you can set these configurations eg. ip with the string "123.123.123.123", so you don't soft-brick your device. And we apologize that there isn't such an example yet, we will be adding it soon.

      Hi erik,

      Ah wish I saw this sooner. This method setStaticIPv4(std::string ip, std::string mask, std::string gateway) is exactly what I was looking for: https://docs.luxonis.com/projects/api/en/latest/references/python/?highlight=.readConfigData()#depthai.DeviceBootloader.Config.setStaticIPv4.

      But now I'm a bit afraid the steps I've described thus far may have soft-bricked the device. Is there a way to reset the OAK-1 POE? I do see a Module Reset Button labelled G here although I'm not sure if it is relevant: "https://docs.luxonis.com/projects/hardware/en/latest/pages/SJ2096POE.html"

      • erik replied to this.

        Hi erik , Running fix_softbrick.py in the linked announcement worked! Now I can connect to my device.

        I will wait for an example on how to use the bootloader API for (.setStaticIPv4(ipv4, mask, gateway), .setDynamicIPv4(ipv4, mask, gateway)).

        I wrote a script to use the bootloader API which shows that the .getIPv4(), .getIPv4Mask(), .getIPv4Gateway(), .isStaticIPV4() are set, but when I rerun the same script I do not see the changes to the bootloader just made.

        # usr/bin/env/python3
        
        import depthai as dai
        
        current_ip = input("Enter IP of POE OAK Device: ").strip() # example: 172.16.15.17
        found_poe, poe = dai.DeviceBase.getDeviceByMxId(current_ip)
        
        if not found_poe:
            print(f"Did not find {current_ip}, exiting.")
            exit(1)
        
        print(f"Found {current_ip}")
        poebl = dai.DeviceBootloader(poe)
        device_config = poebl.Config()
        
        def see_config():
        	print("\nCurrent IPv4 Configurations: ")
        	print(f"IPv4:           {device_config.getIPv4()}")
        	print(f"IPv4 Mask:      {device_config.getIPv4Mask()}") 
        	print(f"IPv4 Gateway:   {device_config.getIPv4Gateway()}")
        	print(f"Is IPv4 static: {device_config.isStaticIPV4()}")
        
        def enter_new_config():
        	print("\nNew IPv4 Configurations: ")
        	ipv4 = input("Enter IPv4: ").strip()
        	mask = input("Enter IPv4 Mask: ").strip()
        	gateway = input("Enter IPv4 Gateway: ").strip()
        	is_static = True if input("Is this a static IPv4 (sets dynamic IPv4 if no)? (y/n): ").strip() == "y" else False
        
        	return ipv4, mask, gateway, is_static
        
        def confirm_new_config(ipv4, mask, gateway, is_static):
        	print("\nEntered IPv4 Configurations: ")
        	print(f"New IPv4:           {ipv4}")
        	print(f"New IPv4 Mask:      {mask}") 
        	print(f"New IPv4 Gateway:   {gateway}")
        	print(f"Is New IPv4 static: {is_static}")
        
        	if is_static:
        		print("Note: Static IPv4 will not start DHCP client")
        	else:
        		print("Note: Dynamic IPv4 will be used (sets IP and starts DHCP client)")
        
        	is_confirmed = True if input("\nPlease confirm this Configuration to Set (y/n): ").strip() == "y" else False
        
        	if not is_confirmed:
        		print("New Config is not Confirmed, exiting.")
        		exit(1)
        
        def perform_config(ipv4, mask, gateway, is_static):
        	print("Setting New Configurations...")
         	
        	if is_static:
         		device_config.setStaticIPv4(ipv4, mask, gateway)
        	else:
        		device_config.setDynamicIPv4(ipv4, mask, gateway)
        	
        	print("Configuration Complete")
        
        def run_config():
            see_config()
            ipv4, mask, gateway, is_static = enter_new_config()
            confirm_new_config(ipv4, mask, gateway, is_static)
            perform_config(ipv4, mask, gateway, is_static)
            see_config()
        
        if __name__ == "__main__":
        	run_config()
          5 days later

          erik any idea when an example will be added. I'm a newbie on these cameras and any help is appreciated.

          gbanuru I was able to run your script, but I'm not seeing the updates when I rerun your script.

          Hi gbanuru
          Sorry for delay on this.

          To read out config from device do the following:

          device_config = poebl.readConfig()

          Your current code just creates a new empty dai.DeviceBootloader.Config object.

          To flash the config run the following at the end (just editing the dai.DeviceBootloader.Config object will not "save" the configuration)

          poebl.flashConfig(device_config)

          Regards, Martin

          I just tried the code below and it correctly sets a static IP, which it keeps even after a power reset. Could you try it out? Thanks!

          import depthai as dai
          
          (found, info) = dai.DeviceBootloader.getFirstAvailableDevice()
          
          def check_str(s: str):
              spl = s.split(".")
              if len(spl) != 4:
                  raise ValueError(f"Entered value {s} doesn't contain 3 dots. Value has to be in the following format: '255.255.255.255'")
              for num in spl:
                  if 255 < int(num):
                      raise ValueError("Entered values can't be above 255!")
              return s
          
          if found:
              print(f'Found device with name: {info.desc.name}');
              with dai.DeviceBootloader(info) as bl:
                  conf = dai.DeviceBootloader.Config()
          
                  ipv4 = check_str(input("Enter IPv4: ").strip())
                  mask = check_str(input("Enter IPv4 Mask: ").strip())
                  gateway = check_str(input("Enter IPv4 Gateway: ").strip())
          
                  val = input(f"Flashing static IPv4 {ipv4}, mask {mask}, gateway {gateway} to the POE device. Enter 'y' to confirm. ").strip()
                  if val != 'y':
                      raise Exception("Flashing aborted.")
          
                  conf.setStaticIPv4(ipv4, mask, gateway)
                  (success, error) = bl.flashConfig(conf)
          
                  if not success:
                      print(f"Error occured while flashing the boot config: {error}")
                  else:
                      print(f"Boot config flashed successfully")

            erik , I'm getting the following error:

            AttributeError: type object 'depthai.DeviceBootloader' has no attribute 'Config

            • erik replied to this.

              Cmitchell63 Please update the library to the latest version, python3 -mpip install depthai -U
              Thanks, Erik

                erik done -- the script runs, showing " Boot config flashed successfully", although the script just hangs after that.