diff --git a/.github/workflows/update-cppcheck.yml b/.github/workflows/update-cppcheck.yml index 6a2816fb..5fd4a51e 100644 --- a/.github/workflows/update-cppcheck.yml +++ b/.github/workflows/update-cppcheck.yml @@ -17,7 +17,11 @@ jobs: - name: Get latest cppcheck version id: version run: | - LATEST=$(curl -fsSL https://api.github.com/repos/danmar/cppcheck/releases/latest | jq -r .tag_name) + # Tags are sorted by time (newest first), so get the first version-like tag + LATEST=$(curl -fsSL "https://api.github.com/repos/danmar/cppcheck/tags?per_page=20" | \ + jq -r '.[].name' | \ + grep -E '^[0-9]+\.[0-9]+(\.[0-9]+)?$' | \ + head -n 1) echo "vers=$LATEST" >> "$GITHUB_OUTPUT" - name: Update VERS in install.sh run: | diff --git a/SConscript b/SConscript index d03a2104..957982ca 100644 --- a/SConscript +++ b/SConscript @@ -139,8 +139,8 @@ base_project_h7 = { # Common autogenerated includes with open("board/obj/gitversion.h", "w") as f: version = get_version(BUILDER, BUILD_TYPE) - f.write(f'extern const uint8_t gitversion[{len(version)}];\n') - f.write(f'const uint8_t gitversion[{len(version)}] = "{version}";\n') + f.write(f'extern const uint8_t gitversion[{len(version)+1}];\n') + f.write(f'const uint8_t gitversion[{len(version)+1}] = "{version}";\n') with open("board/obj/version", "w") as f: f.write(f'{get_version(BUILDER, BUILD_TYPE)}') diff --git a/board/drivers/usb.h b/board/drivers/usb.h index 1f715fee..d393d7e4 100644 --- a/board/drivers/usb.h +++ b/board/drivers/usb.h @@ -502,11 +502,8 @@ static void usb_setup(void) { control_req.length = setup.b.wLength.w; resp_len = comms_control_handler(&control_req, response); - // response pending if -1 was returned - if (resp_len != -1) { - USB_WritePacket(response, MIN(resp_len, setup.b.wLength.w), 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - } + USB_WritePacket(response, MIN(resp_len, setup.b.wLength.w), 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; } } diff --git a/board/flasher.h b/board/flasher.h index 34d93520..f816abec 100644 --- a/board/flasher.h +++ b/board/flasher.h @@ -83,7 +83,7 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { case 0xd6: COMPILE_TIME_ASSERT(sizeof(gitversion) <= USBPACKET_MAX_SIZE); memcpy(resp, gitversion, sizeof(gitversion)); - resp_len = sizeof(gitversion); + resp_len = sizeof(gitversion) - 1U; break; // **** 0xd8: reset ST case 0xd8: diff --git a/board/jungle/flash.py b/board/jungle/flash.py index 8378169e..6f60f70b 100755 --- a/board/jungle/flash.py +++ b/board/jungle/flash.py @@ -12,7 +12,7 @@ if __name__ == "__main__": parser.add_argument("--all", action="store_true", help="Recover all panda jungle devices") args = parser.parse_args() - subprocess.check_call(f"scons -C {board_path}/.. -u -j$(nproc) {board_path}", shell=True) + subprocess.check_call(f"scons -C {board_path}/.. -u -j$(nproc) .", shell=True) if args.all: serials = PandaJungle.list() diff --git a/board/jungle/recover.py b/board/jungle/recover.py index 34c88e26..228f0c56 100755 --- a/board/jungle/recover.py +++ b/board/jungle/recover.py @@ -13,7 +13,7 @@ if __name__ == "__main__": parser.add_argument("--all", action="store_true", help="Recover all panda jungle devices") args = parser.parse_args() - subprocess.check_call(f"scons -C {board_path}/.. -u -j$(nproc) {board_path}", shell=True) + subprocess.check_call(f"scons -C {board_path}/.. -u -j$(nproc) .", shell=True) serials = PandaJungle.list() if args.all else [None] for s in serials: diff --git a/python/__init__.py b/python/__init__.py index 636a86aa..bf1eaf26 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -130,11 +130,6 @@ class Panda: INTERNAL_DEVICES = (HW_TYPE_TRES, HW_TYPE_CUATRO) - MAX_FAN_RPMs = { - HW_TYPE_TRES: 6600, - HW_TYPE_CUATRO: 5000, - } - HARNESS_STATUS_NC = 0 HARNESS_STATUS_NORMAL = 1 HARNESS_STATUS_FLIPPED = 2 @@ -148,9 +143,9 @@ class Panda: self._can_speed_kbps = can_speed_kbps if cli and serial is None: - self._connect_serial = self._cli_select_panda() + self._connect_serial = self._cli_select_panda() else: - self._connect_serial = serial + self._connect_serial = serial # connect and set mcu type self.connect(claim) diff --git a/scripts/fan/fan_test.py b/scripts/fan/fan_test.py index 36a11715..a3872f5c 100755 --- a/scripts/fan/fan_test.py +++ b/scripts/fan/fan_test.py @@ -9,6 +9,6 @@ if __name__ == "__main__": while True: p.set_fan_power(power) time.sleep(5) - print("Power: ", power, "RPM:", str(p.get_fan_rpm()), "Expected:", int(6500 * power / 100)) + print("Power: ", power, "RPM:", str(p.get_fan_rpm())) power += 10 power %= 110 diff --git a/scripts/fan/fan_tuning.py b/scripts/fan/fan_tuning.py deleted file mode 100755 index 60ec72ec..00000000 --- a/scripts/fan/fan_tuning.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python3 -import json -import time -import threading - -from panda import Panda - -def drain_serial(p): - ret = [] - while True: - d = p.serial_read(0) - if len(d) == 0: - break - ret.append(d) - return ret - - -fan_cmd = 0. - -def logger(event): - # requires a build with DEBUG_FAN - with Panda(claim=False) as p, open('/tmp/fan_log', 'w') as f: - power = None - target_rpm = None - rpm_fast = None - t = time.monotonic() - - drain_serial(p) - while not event.is_set(): - p.set_fan_power(fan_cmd) - - for l in drain_serial(p)[::-1]: - ns = l.decode('utf8').strip().split(' ') - if len(ns) == 4: - target_rpm, rpm_fast, power = (int(n, 16) for n in ns) - break - - dat = { - 't': time.monotonic() - t, - 'cmd_power': fan_cmd, - 'pwm_power': power, - 'target_rpm': target_rpm, - 'rpm_fast': rpm_fast, - 'rpm': p.get_fan_rpm(), - } - f.write(json.dumps(dat) + '\n') - time.sleep(1/16.) - p.set_fan_power(0) - -def get_overshoot_rpm(p, power): - global fan_cmd - - # make sure the fan is stopped completely - fan_cmd = 0. - while p.get_fan_rpm() > 100: - time.sleep(0.1) - time.sleep(3) - - # set it to 30% power to mimic going onroad - fan_cmd = power - max_rpm = 0 - max_power = 0 - for _ in range(70): - max_rpm = max(max_rpm, p.get_fan_rpm()) - max_power = max(max_power, p.health()['fan_power']) - time.sleep(0.1) - - # tolerate 10% overshoot - expected_rpm = Panda.MAX_FAN_RPMs[bytes(p.get_type())] * power / 100 - overshoot = (max_rpm / expected_rpm) - 1 - - return overshoot, max_rpm, max_power - - -if __name__ == "__main__": - event = threading.Event() - threading.Thread(target=logger, args=(event, )).start() - - try: - p = Panda() - for power in range(10, 101, 10): - overshoot, max_rpm, max_power = get_overshoot_rpm(p, power) - print(f"Fan power {power}%: overshoot {overshoot:.2%}, Max RPM {max_rpm}, Max power {max_power}%") - finally: - event.set() diff --git a/tests/hitl/7_internal.py b/tests/hitl/7_internal.py index 47c24a47..a7012ebc 100644 --- a/tests/hitl/7_internal.py +++ b/tests/hitl/7_internal.py @@ -5,28 +5,30 @@ from panda import Panda pytestmark = [ pytest.mark.test_panda_types(Panda.INTERNAL_DEVICES), - pytest.mark.test_panda_types([Panda.HW_TYPE_TRES]) ] +MAX_RPM = 5000 + @pytest.mark.timeout(2*60) def test_fan_curve(p): # ensure fan curve is (roughly) linear - for power in (30, 50, 80, 100): - p.set_fan_power(0) - while p.get_fan_rpm() > 0: - time.sleep(0.1) - - # wait until fan spins up, then wait a bit more for the RPM to converge + rpms = [] + for power in (30, 70, 100): + # wait until fan spins up p.set_fan_power(power) for _ in range(20): time.sleep(1) if p.get_fan_rpm() > 1000: break - time.sleep(5) + time.sleep(2) # wait for RPM to converge + rpms.append(p.get_fan_rpm()) + + print(rpms) + diffs = [b - a for a, b in zip(rpms, rpms[1:])] + assert all(x > 0 for x in diffs), f"Fan RPMs not strictly increasing: {rpms=}" + assert rpms[-1] > (0.75*MAX_RPM) - expected_rpm = Panda.MAX_FAN_RPMs[bytes(p.get_type())] * power / 100 - assert 0.75 * expected_rpm <= p.get_fan_rpm() <= 1.25 * expected_rpm def test_fan_cooldown(p): # if the fan cooldown doesn't work, we get high frequency noise on the tach line @@ -35,5 +37,5 @@ def test_fan_cooldown(p): time.sleep(3) p.set_fan_power(0) for _ in range(5): - assert p.get_fan_rpm() <= Panda.MAX_FAN_RPMs[bytes(p.get_type())] + assert p.get_fan_rpm() <= MAX_RPM*2 time.sleep(0.5)