Skip to content

Bring in firmware to monorepo#1

Merged
rossnoah merged 8 commits into
mainfrom
monorepo
Mar 4, 2026
Merged

Bring in firmware to monorepo#1
rossnoah merged 8 commits into
mainfrom
monorepo

Conversation

@rossnoah

@rossnoah rossnoah commented Feb 25, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added motor microcontroller firmware with battery monitoring, motor control, and micro-ROS integration.
    • Added sensor microcontroller firmware with support for multiple sensors (ADC, joystick, fingerprint, IMU, fans, lights, PIR, ultrasonic, lidar) and micro-ROS communication.
    • Implemented automated CI/CD pipelines for all firmware builds.
    • Added ROS 2 message interfaces for inter-system communication.
  • Documentation

    • Added project documentation and setup guides for both firmware projects.

@coderabbitai

coderabbitai Bot commented Feb 25, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@rossnoah has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 48 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 569761d2-d3d5-41d3-9275-d0b23edf5ca3

📥 Commits

Reviewing files that changed from the base of the PR and between 7f74bda and ce501e9.

📒 Files selected for processing (4)
  • .github/workflows/motor_microcontroller_CI.yml
  • .github/workflows/sensor_microcontroller_CI.yml
  • firmware/motor_microcontroller/src/main.cpp
  • firmware/sensor_microcontroller/src/main.cpp
📝 Walkthrough

Walkthrough

This pull request introduces comprehensive firmware development for motor and sensor microcontrollers with microROS integration, GitHub Actions CI/CD workflows for automated testing, and ROS interface message definitions alongside frontend type bindings for cross-system communication.

Changes

Cohort / File(s) Summary
GitHub Actions CI Workflows
.github/workflows/frontend_CI.yml, .github/workflows/motor_microcontroller_CI.yml, .github/workflows/sensor_microcontroller_CI.yml
Three new CI/CD workflows: Frontend workflow with Node.js setup and type checking; Motor Microcontroller workflow building PlatformIO firmware for pico and ROS environments with artifact upload; Sensor Microcontroller workflow building for regular and ROS environments.
Motor Microcontroller Configuration & Documentation
firmware/motor_microcontroller/.gitignore, firmware/motor_microcontroller/README.md, firmware/motor_microcontroller/platformio.ini, firmware/motor_microcontroller/lib/README, firmware/motor_microcontroller/test/README, firmware/motor_microcontroller/include/README
Project setup files: .gitignore for IDE and build artifacts, README with CI badge and motor signal specifications, PlatformIO configuration with three environments (pico, ROS, ROS-debug), and documentation for libraries and testing.
Motor Microcontroller Headers
firmware/motor_microcontroller/include/BatteryFunctions.h, firmware/motor_microcontroller/include/RefSpeed.h, firmware/motor_microcontroller/include/globals.h, firmware/motor_microcontroller/include/microRosFunctions.h
Header declarations: Battery sensing with voltage and percentage calculation; RefSpeed struct for left/right motor speeds; Global speed variables; microROS lifecycle functions (create_entities, destroy_entities, microRosTick), reference speed retrieval, and DAC transmission.
Motor Microcontroller Implementation
firmware/motor_microcontroller/src/BatteryFuctions.cpp, firmware/motor_microcontroller/src/main.cpp, firmware/motor_microcontroller/src/microRosFunctions.cpp
Core firmware: Battery ADC initialization, voltage reading, and percentage calculation; Main control loop with frequency measurement, motor DAC control, direction/brake/enable logic, and conditional ROS integration; microROS setup with publisher/subscriber management for battery, motor speeds, and DAC values with four-state lifecycle management.
Sensor Microcontroller Configuration & Documentation
firmware/sensor_microcontroller/.gitignore, firmware/sensor_microcontroller/README.md, firmware/sensor_microcontroller/platformio.ini, firmware/sensor_microcontroller/lib/README, firmware/sensor_microcontroller/test/README
Project setup: .gitignore covering IDE, build, and KiCad artifacts; README with CI badge and development guidelines; PlatformIO configuration with four environments (regular, debug, ROS, ROS-Debug); Library and test documentation.
Sensor Microcontroller Headers - Core Interfaces
firmware/sensor_microcontroller/include/ADCFunctions.h, firmware/sensor_microcontroller/include/FanFunctions.h, firmware/sensor_microcontroller/include/FingerprintFunctions.h, firmware/sensor_microcontroller/include/IMUFunctions.h, firmware/sensor_microcontroller/include/JoystickFunctions.h
Sensor interface headers: ADC initialization and serial output; Fan control with duty cycle setting, RPM reading, and tachometer support; Fingerprint sensor setup and ID retrieval; IMU initialization, data reading, and output structures; Joystick conversion to speed and displacement with clamping utilities.
Sensor Microcontroller Headers - Additional Sensors
firmware/sensor_microcontroller/include/LidarFunctions.h, firmware/sensor_microcontroller/include/LightFunctions.h, firmware/sensor_microcontroller/include/PIRFunctions.h, firmware/sensor_microcontroller/include/PWMFunctions.h, firmware/sensor_microcontroller/include/UltrasonicFunctions.h, firmware/sensor_microcontroller/include/README
Remaining sensor headers: LiDAR on/off control; Light control with pin definitions; PIR motion sensors with multi-sensor reading; Generic PWM utility; Ultrasonic distance measurement with multi-sensor aggregation; Documentation on header conventions.
Sensor Microcontroller Headers - Integration
firmware/sensor_microcontroller/include/microRosFunctions.h
microROS integration header exposing setup, entity lifecycle, data transmission (thetaRef, omegaRef, sensor data), fingerprint publishing, error reporting, and subscription callbacks for fan, light, and lidar control with ROS/ROS_DEBUG conditional variants.
Sensor Microcontroller Implementation
firmware/sensor_microcontroller/src/ADCFunctions.cpp, firmware/sensor_microcontroller/src/FanFunctions.cpp, firmware/sensor_microcontroller/src/FingerprintFunctions.cpp, firmware/sensor_microcontroller/src/IMUFunctions.cpp, firmware/sensor_microcontroller/src/JoystickFunctions.cpp
Sensor implementations: ADS1115 initialization with retry logic; Fan PWM control and RPM tracking via tachometer interrupts; Fingerprint image capture, matching, and enrollment detection; ICM20948 IMU setup and sensor event reading; Joystick normalization with deadzone and pivot/mix logic for motor speed calculation.
Sensor Microcontroller Implementation - Additional
firmware/sensor_microcontroller/src/LidarFunctions.cpp, firmware/sensor_microcontroller/src/LightFunctions.cpp, firmware/sensor_microcontroller/src/PIRFunctions.cpp, firmware/sensor_microcontroller/src/PWMFunctions.cpp, firmware/sensor_microcontroller/src/UltrasonicFunctions.cpp
Additional sensor implementations: LiDAR GPIO control; Light pin initialization with multi-pin state control (includes fall-through bug in switch); PIR sensor setup and state reading; RP2040 PWM configuration with wrap and channel calculation; Ultrasonic distance scaling from ADC readings with dual-ADC support.
Sensor Microcontroller Main & ROS
firmware/sensor_microcontroller/src/main.cpp, firmware/sensor_microcontroller/src/microRosFunctions.cpp
Main control loop: Sensor initialization, watchdog management, periodic sensor reads (joystick, ultrasonic, PIR, IMU, fingerprint every 5s), conditional ROS messaging with full or reduced payload, and error tracking. microROS implementation: Node/executor setup, publisher/subscriber lifecycle, agent connectivity state machine (WAITING_AGENT, AGENT_AVAILABLE, AGENT_CONNECTED, AGENT_DISCONNECTED), battery/motor/DAC timers, and callback handlers for actuators.
ROS Interface Messages
src/autogiro_interfaces/CMakeLists.txt, src/autogiro_interfaces/msg/Battery.msg, src/autogiro_interfaces/msg/Brake.msg, src/autogiro_interfaces/msg/DacValues.msg, src/autogiro_interfaces/msg/Fingerprint.msg, src/autogiro_interfaces/msg/Lidar.msg, src/autogiro_interfaces/msg/Light.msg, src/autogiro_interfaces/msg/Motors.msg, src/autogiro_interfaces/msg/RefSpeed.msg, src/autogiro_interfaces/msg/SensorError.msg
Nine new ROS 2 message definitions: Battery (percentage), Brake (boolean), DacValues (left/right int16), Fingerprint (user_id), Lidar (state with OFF/ON constants), Light (state with OFF/STEADY/FLASH), Motors (left/right speed in MPH), RefSpeed (left/right speed and displacement), SensorError (four error flags). CMakeLists.txt updated to generate all interfaces.
Frontend Type Definitions
frontend/src/renderer/src/types/ros-msgs.gen.ts
TypeScript type bindings for all nine new ROS messages, providing frontend interfaces for Battery, Brake, DacValues, Fingerprint, Lidar/LidarState, Light/LightState, Motors, RefSpeed, and SensorError with proper field typing (number, boolean).

Sequence Diagram(s)

sequenceDiagram
    participant SensorMCU as Sensor MCU<br/>(RP2040)
    participant ROS as ROS Agent<br/>(Host)
    participant MotorMCU as Motor MCU<br/>(RP2040)
    participant Frontend as Frontend<br/>(React)

    rect rgba(100, 150, 200, 0.5)
    Note over SensorMCU,Frontend: microROS Initialization
    SensorMCU->>ROS: create_entities()
    ROS-->>SensorMCU: entity creation confirmation
    MotorMCU->>ROS: create_entities()
    ROS-->>MotorMCU: entity creation confirmation
    end

    rect rgba(100, 150, 200, 0.5)
    Note over SensorMCU,Frontend: Main Control Loop - Sensor Data Flow
    SensorMCU->>SensorMCU: Read sensors (ADC, IMU, PIR, etc.)
    SensorMCU->>SensorMCU: Calculate omegaRef & thetaRef
    SensorMCU->>ROS: transmitMsg(thetaRef, omegaRef,<br/>ultrasonic, pir, fan, imu)
    ROS->>Frontend: Publish sensor data
    Frontend->>Frontend: Update UI with sensor values
    end

    rect rgba(150, 100, 200, 0.5)
    Note over SensorMCU,Frontend: Motor Control Flow
    ROS->>MotorMCU: Subscription: RefSpeed message
    MotorMCU->>MotorMCU: getRefSpeed()
    MotorMCU->>MotorMCU: Calculate DAC values
    MotorMCU->>MotorMCU: Set motor direction/brake/enable
    MotorMCU->>ROS: transmitDac(leftDAC, rightDAC)
    MotorMCU->>ROS: Publish battery & motor speeds
    ROS->>Frontend: Display battery & motor feedback
    end

    rect rgba(200, 150, 100, 0.5)
    Note over SensorMCU,Frontend: Actuator Control (Subscriptions)
    Frontend->>ROS: User commands (light, lidar, fan)
    ROS->>SensorMCU: Subscription callbacks
    SensorMCU->>SensorMCU: light_subscription_callback()
    SensorMCU->>SensorMCU: lidar_subscription_callback()
    SensorMCU->>SensorMCU: fan_subscription_callback()
    SensorMCU->>SensorMCU: Apply control changes
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

The review spans extensive new firmware across two microcontroller platforms with diverse sensor integrations, microROS communication setup involving state machines and lifecycle management, ROS message interface definitions, and frontend type bindings. While individual sensor modules follow consistent patterns, the breadth of affected areas, interaction complexity between microcontrollers and ROS, and critical control loop logic (motor speed calculation, battery management, actuator commands) demand comprehensive review effort.

Poem

🐰 Whiskers twitch with glee—
Sensors dance in harmony!
Motors spin, ROS flows free,
From pico dreams to messages we see! 🚀✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Bring in firmware to monorepo' clearly and concisely summarizes the main change: integrating firmware code into the repository structure. It directly reflects the substantial addition of firmware files across motor_microcontroller and sensor_microcontroller directories.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch monorepo

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

🟠 Major comments (21)
firmware/sensor_microcontroller/README.md-1-1 (1)

1-1: ⚠️ Potential issue | 🟠 Major

CI badge points to the old repository — will display incorrectly or always fail.

The badge URL references WheelchairSeniorDesign/SensorMicrocontroller, the previous standalone repo. Now that this code lives in TourGuideSeniorDesign/tour-guide-code-module, the badge should point to the new monorepo's workflow.

📝 Proposed fix
-[![PlatformIO CI](https://github.com/WheelchairSeniorDesign/SensorMicrocontroller/actions/workflows/Platformio_CI.yml/badge.svg)](https://github.com/WheelchairSeniorDesign/SensorMicrocontroller/actions/workflows/Platformio_CI.yml)
+[![PlatformIO CI](https://github.com/TourGuideSeniorDesign/tour-guide-code-module/actions/workflows/Platformio_CI.yml/badge.svg)](https://github.com/TourGuideSeniorDesign/tour-guide-code-module/actions/workflows/Platformio_CI.yml)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/README.md` at line 1, Update the CI badge
markdown in README.md so it points to the new monorepo: replace the repository
path WheelchairSeniorDesign/SensorMicrocontroller with
TourGuideSeniorDesign/tour-guide-code-module in the badge URL and link (the
badge line containing Platformio_CI.yml), ensuring both the image src and the
href reference the new repo/workflow path.
firmware/sensor_microcontroller/src/PWMFunctions.cpp-15-17 (1)

15-17: ⚠️ Potential issue | 🟠 Major

Clock divider required for frequencies below ~1.9 kHz; current code will silently produce incorrect PWM output.

The fixed 125 MHz clock with a 16-bit wrap register limits frequencies to f = 125,000,000 / (wrap + 1), where wrap ≤ 65535. This yields a minimum frequency of ~1,907 Hz without additional division. The current code does not call pwm_set_clkdiv(), so any request below 1.9 kHz will overflow the 16-bit register and produce silent incorrect output (e.g., 1000 Hz request truncates wrap to ~59463, generating ~2.1 kHz instead).

Either validate input frequencies and reject values below the minimum, or implement pwm_set_clkdiv() to extend the range down to ~7.5 Hz (with divider ≈ 256).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/PWMFunctions.cpp` around lines 15 - 17,
The code currently assumes a fixed clock_freq and directly computes wrap causing
16-bit overflow for frequencies below ~1.9 kHz; update the PWMFunctions.cpp
logic around clock_freq, wrap, and pwm_set_wrap to compute and set an
appropriate clock divider via pwm_set_clkdiv(slice, clkdiv) when needed: compute
desired_wrap = clock_freq / (frequency * clkdiv) - 1, choose the smallest clkdiv
(float between 1.0 and 256.0) that yields desired_wrap <= 65535, clamp
desired_wrap to uint16_t and call pwm_set_clkdiv(slice, clkdiv) then
pwm_set_wrap(slice, wrap), and if no clkdiv in [1,256] produces a valid wrap,
reject the requested frequency (return error) or clamp to the lowest supported
frequency; reference the variables/function names clock_freq, wrap,
pwm_set_wrap, pwm_set_clkdiv and ensure proper handling of types (float divider,
uint16_t wrap) and edge cases.
firmware/sensor_microcontroller/extra_packages/extra_packages.repos-1-5 (1)

1-5: ⚠️ Potential issue | 🟠 Major

Same self-referential dependency and floating version as the motor counterpart

This file is identical to firmware/motor_microcontroller/extra_packages/extra_packages.repos and has the same two problems: the dependency URL is the parent repo itself, and version: main is a non-pinned floating reference. See the comments on that file for details and the proposed fix.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/extra_packages/extra_packages.repos` around
lines 1 - 5, The extra_packages.repos currently declares a self-referential
repository entry "tour-guide-code-module" pointing to the parent repo URL and
uses a floating "version: main"; replace the URL with the actual external
submodule repo (do not point to the parent repository) and pin "version:" to a
fixed tag or commit SHA (e.g., a release tag or specific commit) instead of
"main" so the dependency is not floating and cannot create
circular/self-dependency issues; update the "tour-guide-code-module" entry
accordingly.
firmware/motor_microcontroller/include/globals.h-8-9 (1)

8-9: ⚠️ Potential issue | 🟠 Major

Missing volatile on shared globals written from micro-ROS callbacks

speedR and speedL are written in the micro-ROS subscription callback (microRosFunctions.cpp) and read in the main control loop. Without volatile, the compiler is free to cache these values in registers and never re-read from memory, so the motor loop may never observe updated reference speeds.

🔧 Proposed fix
-extern float speedR;
-extern float speedL;
+extern volatile float speedR;
+extern volatile float speedL;

Apply the same volatile qualifier to the definitions in the corresponding .cpp file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/motor_microcontroller/include/globals.h` around lines 8 - 9, Declare
the shared globals speedR and speedL as volatile in globals.h and also mark
their definitions in the corresponding .cpp file (the file where they are
currently defined, e.g., globals.cpp) as volatile so updates made by the
micro-ROS subscription callback in microRosFunctions.cpp are always read by the
main control loop; update both the extern declarations (speedR, speedL) and the
actual definitions to use the volatile qualifier to prevent compiler caching.
.github/workflows/sensor_microcontroller_CI.yml-46-46 (1)

46-46: ⚠️ Potential issue | 🟠 Major

if-no-files-found: warn silently passes CI when firmware is missing

If the build output path (firmware/sensor_microcontroller/.pio/build/ROS/firmware.uf2) is wrong or the ROS build fails to produce the artifact, the workflow still completes green with just a warning. Set this to error so a missing artifact is a hard failure.

-          if-no-files-found: warn
+          if-no-files-found: error
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/sensor_microcontroller_CI.yml at line 46, Change the
artifact upload step to fail the run on missing firmware by replacing the
actions/upload-artifact option if-no-files-found: warn with if-no-files-found:
error for the step that uploads the path
"firmware/sensor_microcontroller/.pio/build/ROS/firmware.uf2" (look for the
upload-artifact step using that path) so a missing or misbuilt firmware causes
the workflow to fail rather than pass with a warning.
.github/workflows/motor_microcontroller_CI.yml-46-46 (1)

46-46: ⚠️ Potential issue | 🟠 Major

if-no-files-found: warn silently passes CI when firmware is missing

Same issue as the sensor CI workflow — a wrong path or failed ROS build still produces a green run. Set to error.

-          if-no-files-found: warn
+          if-no-files-found: error
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/motor_microcontroller_CI.yml at line 46, Change the
workflow step that currently uses the runner option if-no-files-found: warn to
if-no-files-found: error so missing firmware (or wrong paths/failed builds) fail
the CI; locate the line with the YAML key if-no-files-found and replace its
value from warn to error to ensure the job errors instead of passing silently.
firmware/sensor_microcontroller/include/PIRFunctions.h-6-7 (1)

6-7: ⚠️ Potential issue | 🟠 Major

Make the header self-contained for uint8_t usage.

At Line 26, uint8_t is used without including a defining header in this file. Add <stdint.h> (or <Arduino.h>) here to avoid include-order-dependent compilation.

Suggested fix
 `#ifndef` PIRFUNCTIONS_H
 `#define` PIRFUNCTIONS_H
+
+#include <stdint.h>

Also applies to: 26-26

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/include/PIRFunctions.h` around lines 6 - 7,
PIRFunctions.h currently uses uint8_t (e.g., in function signatures or typedefs)
without including a defining header; make the header self-contained by adding an
include for <stdint.h> (or <Arduino.h>) at the top of PIRFunctions.h so uint8_t
is always defined regardless of include order, ensuring symbols like uint8_t
used in this file compile reliably.
firmware/sensor_microcontroller/src/LightFunctions.cpp-24-30 (1)

24-30: ⚠️ Potential issue | 🟠 Major

Default branch should be fail-safe, not “light on”.

At Lines 27-29, invalid lightState values drive the same output as case 2. That can enable lights unexpectedly for out-of-range inputs. Add a break for case 2 and make default explicitly off.

Suggested fix
         case 2:
             digitalWrite(LIGHT_PIN_1, HIGH);
             digitalWrite(LIGHT_PIN_2, LOW);
+        break;
         default:
-            digitalWrite(LIGHT_PIN_1, HIGH);
-            digitalWrite(LIGHT_PIN_2, LOW);
+            digitalWrite(LIGHT_PIN_1, LOW);
+            digitalWrite(LIGHT_PIN_2, LOW);
         break;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/LightFunctions.cpp` around lines 24 - 30,
The default branch is currently enabling lights for invalid lightState values;
in the switch handling (case 2 / default) ensure case 2 ends with a break so it
doesn't fall through, and change the default branch to a fail-safe that
explicitly turns lights off by writing LOW to LIGHT_PIN_1 and LIGHT_PIN_2
instead of HIGH. Locate the switch in LightFunctions.cpp and update case 2 to
include a break and set default to digitalWrite(LIGHT_PIN_1, LOW) and
digitalWrite(LIGHT_PIN_2, LOW).
firmware/sensor_microcontroller/include/UltrasonicFunctions.h-21-23 (1)

21-23: ⚠️ Potential issue | 🟠 Major

Return type conflicts with documented error sentinel.

At Line 21, docs specify -1 on misconfiguration, but Line 23 returns uint16_t. That converts -1 to 65535, which breaks the stated API contract.

Suggested fix
-uint16_t ultrasonicDistance(Adafruit_ADS1115 &adc, uint8_t pinNumber);
+int16_t ultrasonicDistance(Adafruit_ADS1115 &adc, uint8_t pinNumber);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/include/UltrasonicFunctions.h` around lines
21 - 23, The function ultrasonicDistance currently returns uint16_t but the
comment says it returns -1 on misconfiguration, causing a mismatch; change the
return type to a signed integer (e.g., int32_t or int16_t) in the
UltrasonicFunctions.h declaration and in the matching implementation (the
ultrasonicDistance definition) and update all callers to handle the negative
sentinel correctly; ensure the chosen signed type can represent all valid
distances and -1 sentinel.
firmware/motor_microcontroller/src/BatteryFuctions.cpp-6-7 (1)

6-7: ⚠️ Potential issue | 🟠 Major

Avoid hardcoded ADC channel when ADC_PIN is configurable.

At Line 7, adc_select_input(2) can become incorrect if ADC_PIN changes. Derive channel from ADC_PIN to keep pin/channel mapping consistent.

Suggested fix
 void initBatterySensor()
 {
     adc_init();
     adc_gpio_init(ADC_PIN);
-    adc_select_input(2);
+    adc_select_input(ADC_PIN - 26); // RP2040 GPIO26..29 -> ADC0..3
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/motor_microcontroller/src/BatteryFuctions.cpp` around lines 6 - 7,
The code hardcodes ADC channel 2 after calling adc_gpio_init(ADC_PIN); replace
the fixed call to adc_select_input(2) with a derived channel from the configured
pin by using the SDK helper that maps a GPIO to its ADC channel (e.g.,
adc_gpio_to_channel(ADC_PIN)), and pass that result to adc_select_input; also
add a simple check for an invalid mapping (negative/invalid channel) and
handle/log it before calling adc_select_input to avoid mis-selection when
ADC_PIN changes.
firmware/sensor_microcontroller/platformio.ini-62-63 (1)

62-63: ⚠️ Potential issue | 🟠 Major

ROS-Debug should also define ROS to keep feature gating consistent.

At Line 63, ROS-Debug only defines ROS_DEBUG. If source files gate ROS code behind #ifdef ROS, this environment can silently diverge from env:ROS.

Suggested fix
 [env:ROS-Debug]
 ...
 build_flags =
+    -D ROS
     -D ROS_DEBUG
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/platformio.ini` around lines 62 - 63, The
build flag currently only defines ROS_DEBUG (build_flags containing "-D
ROS_DEBUG"); update the build_flags so ROS is also defined (add "-D ROS") to
keep feature gating consistent with env:ROS; specifically modify the build_flags
entry that contains "-D ROS_DEBUG" to include both "-D ROS" and "-D ROS_DEBUG"
so source-level checks like `#ifdef` ROS behave the same across environments.
firmware/sensor_microcontroller/include/IMUFunctions.h-30-30 (1)

30-30: ⚠️ Potential issue | 🟠 Major

imuInit returns true on failure — inverted convention.

Looking at the implementation, imuInit returns true when initialization fails (after 10 retries) and false on success. This is the opposite of the near-universal C/C++ convention where true/non-zero means success. Call sites using if (imuInit(...)) expecting success will silently skip initialization errors.

Consider inverting the return semantics or switching to an enum/error code for clarity.

Also minor: parameter names accelRang / gyroRang appear to be typos for accelRange / gyroRange.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/include/IMUFunctions.h` at line 30, The
imuInit function currently returns true on failure and false on success; invert
the return semantics so imuInit(Adafruit_ICM20948 &icm, icm20948_accel_range_t
accelRange, icm20948_gyro_range_t gyroRange, ak09916_data_rate_t magDataRate)
returns true on successful init and false on failure (or replace with an
explicit enum/error code), update all call sites that check if (imuInit(...))
accordingly, and rename the parameters accelRang->accelRange and
gyroRang->gyroRange in the declaration and implementation to fix the typos
consistently.
firmware/sensor_microcontroller/include/FanFunctions.h-47-53 (1)

47-53: 🛠️ Refactor suggestion | 🟠 Major

Remove static interrupt handler declarations from the header.

static functions in a header get internal linkage per translation unit — each .cpp that includes this header gets its own (unused) copy of these ISR stubs. Only the definitions in FanFunctions.cpp are registered with attachInterrupt, so these declarations add no value and may trigger "unused function" warnings.

Keep them solely in FanFunctions.cpp (as static definitions or in an anonymous namespace).

Suggested fix
-static void handleTach0Interrupt();
-
-static void handleTach1Interrupt();
-
-static void handleTach2Interrupt();
-
-static void handleTach3Interrupt();
-
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/include/FanFunctions.h` around lines 47 - 53,
Remove the four static ISR declarations (handleTach0Interrupt,
handleTach1Interrupt, handleTach2Interrupt, handleTach3Interrupt) from the
header so they no longer produce per-translation-unit internal-linkage stubs;
keep the actual static definitions in FanFunctions.cpp (or move them into an
anonymous namespace) where they are registered with attachInterrupt, ensuring
only the real definitions exist and no unused static prototypes remain in the
header.
firmware/sensor_microcontroller/include/FingerprintFunctions.h-14-14 (1)

14-14: ⚠️ Potential issue | 🟠 Major

loopFingerprint() silently drops the fingerprint ID — data is never published.

The implementation in firmware/sensor_microcontroller/src/FingerprintFunctions.cpp (lines 132–136) calls getFingerprintID() but discards its return value, and never invokes publishFingerprint(). Any firmware main loop that relies solely on loopFingerprint() to drive fingerprint data onto the ROS graph will receive nothing.

Either capture and publish the ID inside loopFingerprint(), or document that callers must call getFingerprintID() directly and pipe the result to publishFingerprint().

🐛 Proposed fix in FingerprintFunctions.cpp
 void loopFingerprint()
 {
-  getFingerprintID();
+  uint8_t id = getFingerprintID();
+  if (id != FINGERPRINT_NOFINGER && id != FINGERPRINT_NOTFOUND) {
+    publishFingerprint(id);
+  }
   delay(50);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/include/FingerprintFunctions.h` at line 14,
loopFingerprint() currently calls getFingerprintID() and discards its result, so
fingerprint data is never published; modify loopFingerprint() to store the
return value of getFingerprintID() (e.g., int id = getFingerprintID()) and then
call publishFingerprint(id) when a valid ID is obtained, ensuring any
error/invalid return values from getFingerprintID() are handled (no publish).
Alternatively, if you prefer not to change loopFingerprint(), update the
header/docs to state clearly that callers must call getFingerprintID() and then
pass that result to publishFingerprint() themselves; reference functions:
loopFingerprint, getFingerprintID, publishFingerprint in
FingerprintFunctions.cpp/h.
firmware/sensor_microcontroller/include/FingerprintFunctions.h-12-12 (1)

12-12: ⚠️ Potential issue | 🟠 Major

setupFingerprint() has an inverted return convention — true signals failure.

In firmware/sensor_microcontroller/src/FingerprintFunctions.cpp (line ~104), the function returns true when the sensor can't be found after 10 retries (the failure path), and return false at the end of the function body (the success path). Any caller writing if (setupFingerprint()) { /* proceed */ } will proceed on failure and abort on success.

🐛 Proposed fix in FingerprintFunctions.cpp
       if (init_count > 10) {
-        return true;   // currently: true = sensor not found (failure)
+        return false;  // false = setup failed
       }
       init_count++;
     }
   }
   // ... (successful path at end of function)
-  return false;
+  return true;   // true = setup succeeded
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/include/FingerprintFunctions.h` at line 12,
The setupFingerprint() function currently uses inverted semantics (returns true
on failure and false on success); change the implementation in
FingerprintFunctions.cpp so that setupFingerprint() returns true on successful
initialization and false on failure: flip the return at the retry-failure branch
(where it currently returns true after 10 failed attempts) to return false, and
change the final return false at the end of the function to return true; ensure
any early-exit error returns also follow the same convention and update any
callers if they relied on the old inverted behavior.
src/autogiro_interfaces/msg/RefSpeed.msg-3-4 (1)

3-4: ⚠️ Potential issue | 🟠 Major

lat_disp and long_disp are silently discarded and not processed.

The ROS message RefSpeed.msg includes these fields (lines 3-4), but the firmware's getRefSpeed() function (at lines 279-284 of firmware/motor_microcontroller/src/microRosFunctions.cpp) only extracts and maps left_speed and right_speed to the firmware struct. The displacement fields are received but never used, leading to data loss. Either remove these fields from the ROS message if they are unneeded, or add them to the firmware struct and implement proper handling.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/autogiro_interfaces/msg/RefSpeed.msg` around lines 3 - 4, The
RefSpeed.msg fields lat_disp and long_disp are declared but never used by the
firmware; update the firmware to consume them or remove them from the message.
If keeping them, add matching fields (e.g., int8_t lat_disp; int8_t long_disp;)
to the firmware RefSpeed struct/type and populate them in getRefSpeed()
alongside left_speed and right_speed (convert ROS int8 to the firmware integer
type), then propagate those fields to any downstream logic that needs
lateral/longitudinal displacement; otherwise remove lat_disp and long_disp from
RefSpeed.msg so the message and getRefSpeed() remain consistent.
firmware/motor_microcontroller/src/main.cpp-220-240 (1)

220-240: ⚠️ Potential issue | 🟠 Major

transmitDac called twice per loop when ROS_DEBUG is defined.

When ROS_DEBUG is defined, defined(ROS) || defined(ROS_DEBUG) at line 220 is true (calling transmitDac at line 226), and #ifdef ROS_DEBUG at line 238 is also true (calling transmitDac again at line 239). This results in a double publish every loop iteration.

🐛 Proposed fix — guard the second call
-#ifdef ROS_DEBUG
-    transmitDac(refSpeedL, refSpeedR);
-#endif

The first transmitDac call on line 226 already covers ROS_DEBUG via the defined(ROS) || defined(ROS_DEBUG) guard.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/motor_microcontroller/src/main.cpp` around lines 220 - 240, The code
calls transmitDac(refSpeedL, refSpeedR) twice when ROS_DEBUG is defined due to
both the `#if` defined(ROS) || defined(ROS_DEBUG) block and the later `#ifdef`
ROS_DEBUG block; fix by ensuring only one call per loop: either remove the later
transmitDac(...) in the `#ifdef` ROS_DEBUG block or change its guard to exclude
ROS (e.g., `#ifdef` ROS_DEBUG && !defined(ROS)), so transmitDac is invoked exactly
once; update the blocks surrounding transmitDac to use the transmitDac symbol
and the ROS/ROS_DEBUG preprocessor flags accordingly.
firmware/motor_microcontroller/src/main.cpp-85-94 (1)

85-94: ⚠️ Potential issue | 🟠 Major

Infinite loop if DAC hardware is absent.

Both while (!dacA.begin(...)) and while (!dacB.begin(...)) have no retry limit. If a DAC is disconnected or faulty, the board hangs permanently. Other init functions in this project (IMU, ADC, fingerprint) cap retries at ~10.

🛡️ Proposed fix — add retry limit
+    int dacRetry = 0;
-    while (!dacA.begin(0x62))
+    while (!dacA.begin(0x62) && dacRetry < 10)
     {
         Serial.println("DAC A not found");
         delay(500);
+        dacRetry++;
     }
+    dacRetry = 0;
-    while (!dacB.begin(0x63))
+    while (!dacB.begin(0x63) && dacRetry < 10)
     {
         Serial.println("DAC B not found");
         delay(500);
+        dacRetry++;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/motor_microcontroller/src/main.cpp` around lines 85 - 94, The
infinite-blocking DAC init loops using dacA.begin(...) and dacB.begin(...)
should be changed to bounded retry loops (matching other inits like
IMU/ADC/fingerprint) — replace each while(!dacX.begin(addr)) with a
for/attempt-count retry (e.g., max 10 attempts), call Serial.println on each
failure as now, and after exceeding retries log a final error and proceed with a
safe fallback (returning an error, setting a "dac_failed" flag, or continuing
without blocking) so the MCU doesn't hang if the hardware is absent.
firmware/sensor_microcontroller/src/UltrasonicFunctions.cpp-17-21 (1)

17-21: ⚠️ Potential issue | 🟠 Major

return -1 on uint16_t silently wraps to 65535.

Returning -1 from a uint16_t function yields 65535 (UINT16_MAX) due to unsigned wrap-around. The caller cannot distinguish this from a valid (albeit large) distance reading. Either change the return type or use a sentinel that's clearly out of range, or return 0 and log the error.

🐛 Proposed fix — return 0 for invalid pin
 uint16_t ultrasonicDistance(Adafruit_ADS1115 &adc, uint8_t pinNumber){
     if(pinNumber > 3){
         Serial.println("Please select a pin between 0 and 3");
-        return -1;
+        return 0;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/UltrasonicFunctions.cpp` around lines 17
- 21, The function ultrasonicDistance currently returns uint16_t but uses return
-1 which wraps to 65535; change the signature of
ultrasonicDistance(Adafruit_ADS1115 &adc, uint8_t pinNumber) to a signed integer
type (e.g., int16_t or int32_t) so you can return -1 as an error sentinel,
replace the return -1 with that sentinel, and update any callers to handle the
sentinel error value appropriately; ensure any documentation/comments reflect
the new error contract.
firmware/sensor_microcontroller/src/FanFunctions.cpp-14-14 (1)

14-14: ⚠️ Potential issue | 🟠 Major

pulse_count type mismatch: int16_t is too narrow and inconsistent with pulse_count_single.

pulse_count_single (line 12) is uint32_t, but pulse_count[4] is int16_t. An int16_t overflows at 32,767 pulses — at moderate fan speeds this wraps in minutes. When the delta calculation on line 39 spans a wrap boundary, the result cast to uint32_t produces a huge spurious RPM value.

Use uint32_t to match pulse_count_single and eliminate the overflow risk.

🐛 Proposed fix
-static volatile int16_t pulse_count[4] = {0, 0, 0, 0};
-static volatile int16_t rpm[4] = {0, 0, 0, 0};
+static volatile uint32_t pulse_count[4] = {0, 0, 0, 0};
+static int16_t rpm[4] = {0, 0, 0, 0};

Also update last_pulse_count_test on line 30:

-static int16_t last_pulse_count_test[4] = {0, 0, 0, 0};
+static uint32_t last_pulse_count_test[4] = {0, 0, 0, 0};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/FanFunctions.cpp` at line 14, The array
pulse_count is declared as static volatile int16_t which can overflow and is
inconsistent with pulse_count_single (uint32_t); change pulse_count to static
volatile uint32_t pulse_count[4] to match pulse_count_single and prevent 16-bit
wraparound, and also update the type of last_pulse_count_test to a
uint32_t-compatible type (e.g., uint32_t) so comparisons and delta calculations
between pulse_count, pulse_count_single, and last_pulse_count_test use the same
unsigned 32-bit type.
firmware/sensor_microcontroller/src/microRosFunctions.cpp-257-270 (1)

257-270: ⚠️ Potential issue | 🟠 Major

Initialize errorPublisher in microRosSetup before calling publishError.

Line 347 always publishes to errorPublisher, but microRosSetup() never initializes that publisher in the ROS branch. This is a runtime validity issue for the setup path.

🛠️ Suggested fix
 `#ifdef` ROS
     // create publisher
     RCCHECK(rclc_publisher_init_best_effort(
         &sensorPublisher,
         &node,
         ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg, Sensors),
         sensorTopicName));
     // Create fingerprint publisher
     RCCHECK(rclc_publisher_init_default(
         &fingerprintPublisher,
         &node,
         ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg, Fingerprint),
         fingerprintTopicName));
+    // Create error publisher
+    RCCHECK(rclc_publisher_init_default(
+        &errorPublisher,
+        &node,
+        ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg, SensorError),
+        "sensor_error"));

Also applies to: 341-348

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/microRosFunctions.cpp` around lines 257 -
270, The ROS branch in microRosSetup never initializes errorPublisher but
publishError (called later) assumes it is valid; update microRosSetup to call
rclc_publisher_init_default (or appropriate init, matching other publishers) to
initialize errorPublisher using ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces,
msg, Error) and the errorTopicName before any publishError calls; ensure you use
the same errorPublisher variable name and wrap the init with RCCHECK to mirror
sensorPublisher/fingerprintPublisher initialization.
🟡 Minor comments (17)
firmware/sensor_microcontroller/lib/README-5-6 (1)

5-6: ⚠️ Potential issue | 🟡 Minor

Fix wording for readability and grammar.

A few phrases are awkward ("an own separate directory", "a contents of", "find automatically dependent libraries scanning..."). Please tighten these sentences to improve clarity for new contributors.

Also applies to: 30-31, 42-43

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/lib/README` around lines 5 - 6, Rewrite the
awkward sentences in the README: replace "an own separate directory" with "its
own directory", change "a contents of" to "the contents of", and rephrase "find
automatically dependent libraries scanning..." to "automatically find dependent
libraries by scanning..."; apply these same wording fixes to the other
occurrences noted (lines referencing the same phrases).
firmware/sensor_microcontroller/include/README-7-7 (1)

7-7: ⚠️ Potential issue | 🟡 Minor

Fix mismatched inline-code quoting.

#include' and .h' use mixed delimiters; render them with matching markdown backticks for consistency and readability.

✏️ Proposed doc fix
-by including it, with the C preprocessing directive `#include'.
+by including it, with the C preprocessing directive `#include`.

-In C, the usual convention is to give header files names that end with `.h'.
+In C, the usual convention is to give header files names that end with `.h`.

Also applies to: 28-28

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/include/README` at line 7, Fix the mismatched
inline-code quoting by replacing the mixed delimiters around preprocessor tokens
with consistent backticks: render `#include` and file extensions like `.h` using
matching Markdown code ticks (e.g., `#include`, `.h`) in the README text
(affects the current line and the similar occurrence at line 28); update any
other instances in the file that use single quotes around these tokens to use
backticks instead.
firmware/sensor_microcontroller/.gitignore-36-36 (1)

36-36: ⚠️ Potential issue | 🟡 Minor

Missing # — "PlatformIO files" is treated as a literal ignore pattern, not a comment.

Without the # prefix, Git interprets PlatformIO files as a glob pattern to match against file/directory names rather than a section header comment. This is almost certainly unintentional.

🐛 Proposed fix
-PlatformIO files
+# PlatformIO files
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/.gitignore` at line 36, The line "PlatformIO
files" in firmware/sensor_microcontroller/.gitignore is currently treated as a
literal ignore pattern; change it to a comment by prefixing it with "#" (e.g.,
"# PlatformIO files") so it serves as a section header and not a glob pattern,
ensuring patterns below remain effective and readable.
firmware/sensor_microcontroller/README.md-8-8 (1)

8-8: ⚠️ Potential issue | 🟡 Minor

Heading hierarchy skips a level (h1 → h3).

### Development Environent Setup is an h3 directly under the h1 # Contributing Guidelines, violating Markdown heading-increment rules (MD001). It should be h2.

📝 Proposed fix
-### Development Environent Setup
+## Development Environment Setup
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/README.md` at line 8, Change the heading "###
Development Environent Setup" from an h3 to an h2 so it becomes "## Development
Environent Setup" to fix the Markdown heading hierarchy violation (MD001);
locate the string "Development Environent Setup" in README.md and update the
leading hashes accordingly.
firmware/sensor_microcontroller/README.md-4-4 (1)

4-4: ⚠️ Potential issue | 🟡 Minor

Stale product reference: "wheelchair" should reflect the tour guide project.

This README was carried over from the old wheelchair repo. Line 4 should describe the tour guide system.

📝 Proposed fix
-this repo is for all the code related to the microcontroller system for our wheelchair.
+This directory contains all code related to the sensor microcontroller system for the tour guide robot.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/README.md` at line 4, Update the README
sentence that currently reads "this repo is for all the code related to the
microcontroller system for our wheelchair." to reference the tour guide project
instead; change the wording to something like "this repo contains all
microcontroller firmware and related code for the tour guide system" so the
description accurately reflects the project's scope in README.md.
firmware/sensor_microcontroller/README.md-8-24 (1)

8-24: ⚠️ Potential issue | 🟡 Minor

Multiple spelling typos across the document.

The following typos were found:

Line Current Correction
8 Environent Environment
14 Rapspberry Raspberry
21 corrisponding corresponding
24 decription description
📝 Proposed fixes
-### Development Environent Setup
+## Development Environment Setup
-6. Upload the project to the Rapspberry Pi Pico using the PlatformIO upload button.
+6. Upload the project to the Raspberry Pi Pico using the PlatformIO upload button.
-Please put all `.c/cpp` files in the src directory and the corrisponding `.h` files in the include directory.
+Please put all `.c/cpp` files in the src directory and the corresponding `.h` files in the include directory.
-Please write a brief decription of what your code changed, and why you made those changes.
+Please write a brief description of what your code changed, and why you made those changes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/README.md` around lines 8 - 24, Fix multiple
spelling typos in the README: change the section heading "Development Environent
Setup" to "Development Environment Setup", correct "Rapspberry Pi Pico" to
"Raspberry Pi Pico", update "corrisponding `.h` files" to "corresponding `.h`
files", and change "decription" in the Pull Request Guidelines to "description";
ensure these exact replacements are applied in the README.md content so the
heading, the Pico reference, the include-directory guidance, and the PR
guidelines read correctly.
firmware/motor_microcontroller/README.md-1-1 (1)

1-1: ⚠️ Potential issue | 🟡 Minor

Stale CI badge points to a different org and workflow

The badge references WheelchairSeniorDesign/MotorMicrocontroller with workflow Platformio_CI.yml, but this repo is TourGuideSeniorDesign/tour-guide-code-module with workflow motor_microcontroller_CI.yml. The badge's URL is likely still referencing a workflow name or path that doesn't exist. Both the image source and the link target need updating.

🔧 Proposed fix
-[![PlatformIO CI](https://github.com/WheelchairSeniorDesign/MotorMicrocontroller/actions/workflows/Platformio_CI.yml/badge.svg)](https://github.com/WheelchairSeniorDesign/MotorMicrocontroller/actions/workflows/Platformio_CI.yml)
+[![Motor Microcontroller CI](https://github.com/TourGuideSeniorDesign/tour-guide-code-module/actions/workflows/motor_microcontroller_CI.yml/badge.svg)](https://github.com/TourGuideSeniorDesign/tour-guide-code-module/actions/workflows/motor_microcontroller_CI.yml)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/motor_microcontroller/README.md` at line 1, Update the stale CI
badge in the README: change the image source and link target that currently
point to WheelchairSeniorDesign/MotorMicrocontroller and Platformio_CI.yml so
they reference the correct repository
TourGuideSeniorDesign/tour-guide-code-module and the actual workflow filename
motor_microcontroller_CI.yml; ensure both the badge URL and the href use the
same repo/workflow names so the badge image and link resolve to the existing
GitHub Actions workflow.
firmware/sensor_microcontroller/src/ADCFunctions.cpp-21-24 (1)

21-24: ⚠️ Potential issue | 🟡 Minor

Retry limit is off-by-one.

At Line 21, adc_count > 10 allows an extra failed attempt before returning. If the intended max is 10 retries, switch to >= 10 (or increment before the check).

Suggested fix
-        if (adc_count > 10) {
+        if (adc_count >= 10) {
             return true;
         }
         adc_count++;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/ADCFunctions.cpp` around lines 21 - 24,
The retry check for adc_count is off-by-one: currently it allows 11 attempts
because it checks if (adc_count > 10) after incrementing; change the logic to
enforce a maximum of 10 retries by either using if (adc_count >= 10) in place of
if (adc_count > 10) or by incrementing adc_count before the check (move
adc_count++ above the if). Update the condition that references adc_count in
ADCFunctions.cpp so the loop/early return triggers exactly when the 10th retry
is reached.
firmware/sensor_microcontroller/include/FanFunctions.h-18-23 (1)

18-23: ⚠️ Potential issue | 🟡 Minor

FanDutyCycles uses int8_t but setFanIndividual accepts uint8_t for duty cycle.

int8_t has a max of 127, while uint8_t goes to 255. If duty cycles represent 0–255 (common for 8-bit PWM), values above 127 will be misinterpreted as negative when stored in FanDutyCycles. At minimum, aligning the types across the struct and the function signature would prevent subtle truncation bugs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/include/FanFunctions.h` around lines 18 - 23,
The FanDutyCycles struct uses signed int8_t fields while the function
setFanIndividual accepts uint8_t duty values, risking negative values for duty
>127; update FanDutyCycles' members (fan_0_duty_cycle, fan_1_duty_cycle,
fan_2_duty_cycle, fan_3_duty_cycle) to uint8_t to match setFanIndividual (or
alternatively change setFanIndividual to int8_t if the API is intended to be
signed), ensuring the types are consistent to prevent truncation/interpretation
bugs.
firmware/sensor_microcontroller/include/JoystickFunctions.h-18-24 (1)

18-24: ⚠️ Potential issue | 🟡 Minor

Truncated doc comment.

Line 19: "Struct representing the reference displace" is missing the trailing ment.

 /**
- * Struct representing the reference displace
+ * Struct representing the reference displacement.
  */
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/include/JoystickFunctions.h` around lines 18
- 24, Fix the truncated doc comment for the struct by updating the comment text
to read "Struct representing the reference displacement" so the documentation
above the RefDisplacement struct correctly spells "displacement"; locate the
comment immediately preceding struct RefDisplacement and replace the existing
truncated line with the complete phrase.
src/autogiro_interfaces/msg/SensorError.msg-1-4 (1)

1-4: ⚠️ Potential issue | 🟡 Minor

Consider adding a lidar_error field.

The PR includes Lidar.msg and firmware references a lidar_subscription_callback, so the lidar is an active tracked sensor. Omitting it from SensorError.msg means lidar faults have no standard error-reporting channel alongside the other sensors.

✏️ Suggested addition
 bool joystick_adc_error
 bool ultrasonic_adc_error
 bool fingerprint_error
 bool imu_error
+bool lidar_error
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/autogiro_interfaces/msg/SensorError.msg` around lines 1 - 4,
SensorError.msg is missing a lidar_error boolean even though the codebase
includes Lidar.msg and a lidar_subscription_callback that monitors the lidar;
add a new field named lidar_error to SensorError.msg (consistent with the
existing bool fields joystick_adc_error, ultrasonic_adc_error,
fingerprint_error, imu_error) so lidar faults can be reported via the same
sensor error message type and update any publishers/subscribers (e.g.,
lidar_subscription_callback) to set this field when lidar faults are detected.
firmware/sensor_microcontroller/src/PIRFunctions.cpp-16-16 (1)

16-16: ⚠️ Potential issue | 🟡 Minor

Misleading Serial.println in setupPIR() — prints "PIR detected" during initialization.

This string implies a motion detection event, but it fires once at boot during pin configuration. Rename it to avoid confusion during debugging.

✏️ Proposed fix
-    Serial.println("PIR detected");
+    Serial.println("PIR sensors initialized");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/PIRFunctions.cpp` at line 16, In
setupPIR() update the misleading Serial.println("PIR detected") that runs during
initialization to a clear initialization message (e.g., "PIR initialized" or
"PIR setup complete") so it doesn't appear as a motion event; locate the
Serial.println call inside setupPIR() in PIRFunctions.cpp and replace the string
to reflect initialization instead of detection.
firmware/sensor_microcontroller/src/UltrasonicFunctions.cpp-11-11 (1)

11-11: ⚠️ Potential issue | 🟡 Minor

Typo: "dac" should be "adc".

The comment says "16 bit dac" but this value represents the ADC max, not a DAC.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/UltrasonicFunctions.cpp` at line 11, The
comment on the ADC_SOLUTION macro has a typo referencing "dac" instead of "adc";
update the comment next to `#define` ADC_SOLUTION 26230 in UltrasonicFunctions.cpp
to say it represents the maximum ADC value for a 16-bit ADC at 3.3V (e.g.,
change "Max value for a 16 bit dac at 3.3V" to "Max value for a 16-bit ADC at
3.3V" or similar).
firmware/sensor_microcontroller/src/main.cpp-138-138 (1)

138-138: ⚠️ Potential issue | 🟡 Minor

Hardcoded sentinel fingerID = 2 collides with valid fingerprint IDs.

If a fingerprint with database ID 2 exists, it becomes indistinguishable from the "no scan performed" sentinel. Consider using FINGERPRINT_NOFINGER or 0xFF (255) as a sentinel instead, since valid enrolled IDs typically start at low values and 255 is unlikely to be used.

🛡️ Proposed fix
-    uint8_t fingerID = 2;
+    uint8_t fingerID = 0xFF;  // sentinel: no scan performed
     ...
-    if(fingerID != 2){
+    if(fingerID != 0xFF){
         publishFingerprint(fingerID);
     }

Also applies to: 161-163

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/main.cpp` at line 138, The code uses a
hardcoded sentinel uint8_t fingerID = 2 which can collide with real fingerprint
IDs; change the sentinel to an unmistakable value (e.g., use the library
constant FINGERPRINT_NOFINGER or 0xFF) wherever fingerID is initialized or set
(including the declaration in main.cpp and the other occurrences around the
previous 161-163 region) and ensure all logic that checks for "no scan
performed" compares against that new sentinel (and that fingerID remains
uint8_t).
firmware/motor_microcontroller/src/main.cpp-184-193 (1)

184-193: ⚠️ Potential issue | 🟡 Minor

Comments contradict the code for enable and brake signals.

Line 184: comment says "enable the motor controller" but sets enable = false. Line 192: comment says "disable brake" but sets brake = true. If the signals are active-low, please update the comments to reflect that. Misleading comments are worse than no comments.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/motor_microcontroller/src/main.cpp` around lines 184 - 193, The
comments around the enable/brake logic are incorrect relative to the assignments
in main.cpp: the code sets enable = false and sets brake = true when speeds != 0
while comments state the opposite; either update the comments to indicate these
signals are active-low (e.g., "enable is active-low: false -> enabled" and
"brake is active-low: true -> braking") or change the assignments to match the
current English comments. Locate the block using the symbols enable, brake and
the check on refSpeedSensors.rightSpeed / refSpeedSensors.leftSpeed and either
flip the boolean assignments to match the intent in the comments or change the
comment text to explicitly state the active-low semantics so the code and
comments are consistent.
firmware/sensor_microcontroller/src/JoystickFunctions.cpp-129-130 (1)

129-130: ⚠️ Potential issue | 🟡 Minor

Redundant check: abs() result is always ≥ 0.

diff is assigned from abs(...), so it's always non-negative. The condition diff > -diffParam (i.e., diff > -30) is always true. The effective check is just diff < diffParam.

If the intent was to check whether left and right speeds are "close enough," consider dropping the abs() and checking the signed difference:

Option A: simplify with abs
-    int diff = abs(speeds.leftSpeed - speeds.rightSpeed);
-    if(diff > -diffParam && diff < diffParam){
+    int diff = abs(speeds.leftSpeed - speeds.rightSpeed);
+    if(diff < diffParam){
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/JoystickFunctions.cpp` around lines 129 -
130, The current check uses int diff = abs(speeds.leftSpeed - speeds.rightSpeed)
then tests diff > -diffParam && diff < diffParam, but abs(...) is non-negative
so the first comparison is redundant; either remove the unnecessary diff >
-diffParam and keep if (diff < diffParam) to use the absolute-distance
semantics, or if you intended a signed-range check compute int diff =
speeds.leftSpeed - speeds.rightSpeed and replace the condition with if (diff >
-diffParam && diff < diffParam). Update the usage of diff and the conditional
accordingly (symbols: diff, diffParam, speeds.leftSpeed, speeds.rightSpeed).
firmware/sensor_microcontroller/src/FingerprintFunctions.cpp-88-91 (1)

88-91: ⚠️ Potential issue | 🟡 Minor

while (!Serial) can block indefinitely on headless deployments.

On boards without a USB-Serial monitor attached, this loop may never terminate. Consider adding a timeout to prevent hanging in production.

⏱️ Proposed fix with timeout
 bool setupFingerprint()
 {
-  while (!Serial);  // For Yun/Leo/Micro/Zero/...
+  {
+    unsigned long start = millis();
+    while (!Serial && millis() - start < 3000);  // timeout after 3s
+  }
   delay(100);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/FingerprintFunctions.cpp` around lines 88
- 91, The setupFingerprint function uses "while (!Serial)" which can block
indefinitely on headless devices; change this to a non-blocking wait with a
timeout using millis(): record a start time, loop while !Serial but break if
elapsed time exceeds a configurable timeout (e.g., a few seconds), and proceed
whether or not Serial became available; update the Serial wait logic in
setupFingerprint and any related initialization so the device never hangs if no
USB-Serial is attached.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb87bfe and 2d5f25a.

📒 Files selected for processing (57)
  • .github/workflows/frontend_CI.yml
  • .github/workflows/motor_microcontroller_CI.yml
  • .github/workflows/sensor_microcontroller_CI.yml
  • firmware/motor_microcontroller/.gitignore
  • firmware/motor_microcontroller/README.md
  • firmware/motor_microcontroller/extra_packages/extra_packages.repos
  • firmware/motor_microcontroller/include/BatteryFunctions.h
  • firmware/motor_microcontroller/include/README
  • firmware/motor_microcontroller/include/RefSpeed.h
  • firmware/motor_microcontroller/include/globals.h
  • firmware/motor_microcontroller/include/microRosFunctions.h
  • firmware/motor_microcontroller/lib/README
  • firmware/motor_microcontroller/platformio.ini
  • firmware/motor_microcontroller/src/BatteryFuctions.cpp
  • firmware/motor_microcontroller/src/main.cpp
  • firmware/motor_microcontroller/src/microRosFunctions.cpp
  • firmware/motor_microcontroller/test/README
  • firmware/sensor_microcontroller/.gitignore
  • firmware/sensor_microcontroller/README.md
  • firmware/sensor_microcontroller/extra_packages/extra_packages.repos
  • firmware/sensor_microcontroller/include/ADCFunctions.h
  • firmware/sensor_microcontroller/include/FanFunctions.h
  • firmware/sensor_microcontroller/include/FingerprintFunctions.h
  • firmware/sensor_microcontroller/include/IMUFunctions.h
  • firmware/sensor_microcontroller/include/JoystickFunctions.h
  • firmware/sensor_microcontroller/include/LidarFunctions.h
  • firmware/sensor_microcontroller/include/LightFunctions.h
  • firmware/sensor_microcontroller/include/PIRFunctions.h
  • firmware/sensor_microcontroller/include/PWMFunctions.h
  • firmware/sensor_microcontroller/include/README
  • firmware/sensor_microcontroller/include/UltrasonicFunctions.h
  • firmware/sensor_microcontroller/include/microRosFunctions.h
  • firmware/sensor_microcontroller/lib/README
  • firmware/sensor_microcontroller/platformio.ini
  • firmware/sensor_microcontroller/src/ADCFunctions.cpp
  • firmware/sensor_microcontroller/src/FanFunctions.cpp
  • firmware/sensor_microcontroller/src/FingerprintFunctions.cpp
  • firmware/sensor_microcontroller/src/IMUFunctions.cpp
  • firmware/sensor_microcontroller/src/JoystickFunctions.cpp
  • firmware/sensor_microcontroller/src/LidarFunctions.cpp
  • firmware/sensor_microcontroller/src/LightFunctions.cpp
  • firmware/sensor_microcontroller/src/PIRFunctions.cpp
  • firmware/sensor_microcontroller/src/PWMFunctions.cpp
  • firmware/sensor_microcontroller/src/UltrasonicFunctions.cpp
  • firmware/sensor_microcontroller/src/main.cpp
  • firmware/sensor_microcontroller/src/microRosFunctions.cpp
  • firmware/sensor_microcontroller/test/README
  • src/autogiro_interfaces/CMakeLists.txt
  • src/autogiro_interfaces/msg/Battery.msg
  • src/autogiro_interfaces/msg/Brake.msg
  • src/autogiro_interfaces/msg/DacValues.msg
  • src/autogiro_interfaces/msg/Fingerprint.msg
  • src/autogiro_interfaces/msg/Lidar.msg
  • src/autogiro_interfaces/msg/Light.msg
  • src/autogiro_interfaces/msg/Motors.msg
  • src/autogiro_interfaces/msg/RefSpeed.msg
  • src/autogiro_interfaces/msg/SensorError.msg

int16_t refSpeedR; // value sent to the motor controller for speed of right motor
int16_t refSpeedL; // value sent to the motor controller for speed of left motor
int16_t batteryValue;
refSpeed refSpeedSensors;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

refSpeedSensors is uninitialized when ROS/ROS_DEBUG are not defined — garbage motor commands.

refSpeedSensors is only populated inside the #if defined(ROS) || defined(ROS_DEBUG) block (line 145), but it's used unconditionally at lines 185–218 to derive motor direction and speed. When neither macro is defined, the struct contains indeterminate values, which will drive the motors unpredictably.

🐛 Proposed fix — zero-initialize
-refSpeed refSpeedSensors;
+refSpeed refSpeedSensors{};

Or, alternatively, ensure the non-ROS code path also populates refSpeedSensors (e.g., from a serial or direct sensor input).

Also applies to: 184-218

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/motor_microcontroller/src/main.cpp` at line 47, The refSpeedSensors
instance is left uninitialized when ROS/ROS_DEBUG are not defined, causing
garbage motor commands; fix by zero-initializing refSpeedSensors at declaration
(e.g., refSpeedSensors refSpeedSensors = {}; or memset) or ensure the non-ROS
code path populates it before use; update the declaration and/or add a fallback
initialization before any code that derives motor direction/speed from
refSpeedSensors so the struct has deterministic values.

* @param adc - An instance of the Adafruit_ADS1115 class for the ADC
* @param i2c_addr - The I2C address of the ADC in hex
*/
bool adcInit(Adafruit_ADS1115 &adc, uint8_t i2c_addr);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Inverted adcInit return value — true signals failure, false signals success

In firmware/sensor_microcontroller/src/ADCFunctions.cpp (line 21), adcInit returns true when the retry limit is exceeded (i.e., initialization failed) and false on success. This is the opposite of the conventional C++ true = success pattern. Any caller that writes if (adcInit(...)) { /* proceed */ } will proceed with an uninitialised ADC.

Fix the implementation to align with convention, and update the Doxygen to document the semantics:

🐛 Proposed fix in ADCFunctions.cpp
         if (adc_count > 10) {
-            return true;   // was: true = failure
+            return false;  // false = initialization failed
         }
     ...
     }
-    return false;          // was: false = success
+    return true;           // true = initialization succeeded

Also update the @return Doxygen in ADCFunctions.h:

+ * `@return` true if ADC initialized successfully, false on failure
  bool adcInit(Adafruit_ADS1115 &adc, uint8_t i2c_addr);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/include/ADCFunctions.h` at line 16, The
adcInit function currently returns true on failure and false on success; change
its implementation in ADCFunctions.cpp (function adcInit(Adafruit_ADS1115 &adc,
uint8_t i2c_addr)) so it returns true when initialization succeeds and false
when it fails (e.g., after exhausting retries), and update the Doxygen `@return`
in ADCFunctions.h to state "@return true on successful initialization, false on
failure"; ensure all return points in adcInit are inverted accordingly and that
no other logic in adcInit relies on the old inverted convention.

Comment thread firmware/sensor_microcontroller/include/microRosFunctions.h Outdated
Comment thread firmware/sensor_microcontroller/include/microRosFunctions.h

//microRosSetup(1, nodeName, sensorTopicName, fingerprintTopicName);

#elif ROS_DEBUG

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

#elif ROS_DEBUG / #elif DEBUG should use defined().

#elif ROS_DEBUG checks the macro's integer value, not its existence. If the build system defines -DROS_DEBUG (with no value), the macro expands to nothing and the preprocessor evaluates #elif with an empty expression (treated as 0 → false). The #ifdef ROS on line 50 and #if defined(ROS) on line 15 use the correct form. This should be consistent throughout.

🐛 Proposed fix
-    `#elif` ROS_DEBUG
+    `#elif` defined(ROS_DEBUG)

Apply the same fix at line 164 and line 170:

-    `#elif` ROS_DEBUG
+    `#elif` defined(ROS_DEBUG)
     ...
-    `#elif` DEBUG
+    `#elif` defined(DEBUG)

Also applies to: 164-164, 170-170

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/main.cpp` at line 61, The preprocessor
checks use bare "#elif ROS_DEBUG" and "#elif DEBUG" which test numeric value
rather than macro existence; change these to use the defined() form (e.g.
replace "#elif ROS_DEBUG" with "#elif defined(ROS_DEBUG)" and "#elif DEBUG" with
"#elif defined(DEBUG)") so presence-only defines work correctly; update every
occurrence (the ones paired with the existing "#ifdef ROS" and "#if
defined(ROS)" checks and the additional spots noted) to use defined(MACRO) for
consistency.

Comment on lines +71 to +80
watchdog_enable(5000, 1); // updating the watchdog// set the watchdog to run at 5s interval

ultrasonic_adc_error = adcInit(ultrasonicAdc, 0x49); //default address
joystick_adc_error = adcInit(joystickAdc, 0x48); //default address
imu_error = imuInit(icm, ICM20948_ACCEL_RANGE_2_G, ICM20948_GYRO_RANGE_250_DPS, AK09916_MAG_DATARATE_10_HZ);
fingerprint_error = setupFingerprint();
Serial.print("Fingerprint error: ");
setAllFans(startDutyCycles);
setupRPMCounter();
setupLight();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Watchdog enabled before sensor init — risk of reset loop.

The watchdog is armed with a 5-second timeout at line 71, but sensor initialization follows (lines 73–80). The fingerprint sensor retry loop alone can take >5 seconds (up to 11 retries). If the total init time exceeds 5 seconds, the watchdog fires and the board enters a reset loop.

Move the watchdog_enable call to after all initialization is complete, or add watchdog_update() calls within the long init sequences.

🐛 Proposed fix — move watchdog enable after init
-    watchdog_enable(5000, 1);  // updating the watchdog// set the watchdog to run at 5s interval
-
     ultrasonic_adc_error = adcInit(ultrasonicAdc, 0x49);
     joystick_adc_error =  adcInit(joystickAdc, 0x48);
     imu_error = imuInit(icm, ICM20948_ACCEL_RANGE_2_G, ICM20948_GYRO_RANGE_250_DPS, AK09916_MAG_DATARATE_10_HZ);
     fingerprint_error = setupFingerprint();
     ...
     setupRPMCounter();
     setupLight();
+
+    watchdog_enable(5000, 1);  // set the watchdog to run at 5s interval
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/main.cpp` around lines 71 - 80, The
watchdog is enabled with watchdog_enable(5000, 1) before long sensor init calls
(adcInit, imuInit, setupFingerprint) which can exceed 5s and cause reset loops;
move the watchdog_enable call to after all initialization (after
setupLight()/setupRPMCounter()/setAllFans) or, if you must keep it earlier,
insert periodic watchdog_update() calls inside long routines (notably inside
setupFingerprint's retry loop) so the watchdog is refreshed while waiting;
update the code to call watchdog_enable only once initialization is complete or
ensure watchdog_update is called during any multi-second retry loops.

Comment on lines +82 to +87
#ifdef ROS
if (!joystick_adc_error || !ultrasonic_adc_error || !fingerprint_error || !imu_error) {
publishError(joystick_adc_error, ultrasonic_adc_error, fingerprint_error, imu_error);
error_timer = 500;
}
#endif

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Inverted error-publish condition — publishes when sensors are healthy, not when they fail.

!joystick_adc_error || !ultrasonic_adc_error || !fingerprint_error || !imu_error is true when any sensor initialized successfully. This means errors are published even when all sensors are fine, and the reduced error_timer (500ms) is set unnecessarily. The intent appears to be publishing when any sensor failed.

🐛 Proposed fix
-    if (!joystick_adc_error || !ultrasonic_adc_error || !fingerprint_error || !imu_error) {
+    if (joystick_adc_error || ultrasonic_adc_error || fingerprint_error || imu_error) {
         publishError(joystick_adc_error, ultrasonic_adc_error, fingerprint_error, imu_error);
         error_timer = 500;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#ifdef ROS
if (!joystick_adc_error || !ultrasonic_adc_error || !fingerprint_error || !imu_error) {
publishError(joystick_adc_error, ultrasonic_adc_error, fingerprint_error, imu_error);
error_timer = 500;
}
#endif
`#ifdef` ROS
if (joystick_adc_error || ultrasonic_adc_error || fingerprint_error || imu_error) {
publishError(joystick_adc_error, ultrasonic_adc_error, fingerprint_error, imu_error);
error_timer = 500;
}
`#endif`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/main.cpp` around lines 82 - 87, The
condition currently publishes errors when sensors are healthy because it checks
!joystick_adc_error || !ultrasonic_adc_error || !fingerprint_error ||
!imu_error; change it to publish only when any sensor failed by using the plain
error flags: joystick_adc_error || ultrasonic_adc_error || fingerprint_error ||
imu_error, then call publishError(joystick_adc_error, ultrasonic_adc_error,
fingerprint_error, imu_error) and set error_timer = 500 inside that branch; keep
this logic inside the existing `#ifdef` ROS block so the behavior only runs under
ROS.

Comment on lines +129 to +136
#elif ROS_DEBUG
// create publisher
RCCHECK(rclc_publisher_init_best_effort(
&sensorPublisher,
&node,
ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg, RefSpeed),
topicName));
#endif

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -nP 'bool create_entities\s*\(|\btopicName\b' firmware/sensor_microcontroller/src/microRosFunctions.cpp -C3

Repository: TourGuideSeniorDesign/tour-guide-code-module

Length of output: 981


🏁 Script executed:

cat -n firmware/sensor_microcontroller/src/microRosFunctions.cpp | sed -n '85,150p'

Repository: TourGuideSeniorDesign/tour-guide-code-module

Length of output: 2363


🏁 Script executed:

rg -n 'create_entities\s*\(' firmware/sensor_microcontroller/src/microRosFunctions.cpp

Repository: TourGuideSeniorDesign/tour-guide-code-module

Length of output: 201


🏁 Script executed:

cat -n firmware/sensor_microcontroller/src/microRosFunctions.cpp | sed -n '200,250p'

Repository: TourGuideSeniorDesign/tour-guide-code-module

Length of output: 2217


🏁 Script executed:

cat -n firmware/sensor_microcontroller/src/microRosFunctions.cpp | sed -n '1,90p'

Repository: TourGuideSeniorDesign/tour-guide-code-module

Length of output: 2945


🏁 Script executed:

rg -n 'topicName\b' firmware/sensor_microcontroller/src/microRosFunctions.cpp

Repository: TourGuideSeniorDesign/tour-guide-code-module

Length of output: 241


🏁 Script executed:

cat -n firmware/sensor_microcontroller/src/microRosFunctions.cpp | sed -n '230,300p'

Repository: TourGuideSeniorDesign/tour-guide-code-module

Length of output: 2780


Fix undefined topicName in ROS_DEBUG create_entities().

Line 135 references topicName, but create_entities() has no parameters and is called from microRosTick() with no arguments, leaving topicName undefined in scope. ROS_DEBUG builds will fail at compile time. Use a hardcoded topic name instead, matching the ROS build pattern.

🛠️ Suggested fix
 `#elif` ROS_DEBUG
     // create publisher
 RCCHECK(rclc_publisher_init_best_effort(
         &sensorPublisher,
         &node,
         ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg, RefSpeed),
-        topicName));
+        "ref_speed"));
 `#endif`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/microRosFunctions.cpp` around lines 129 -
136, create_entities() references an undefined variable topicName when
initializing sensorPublisher under the ROS_DEBUG branch, causing compile
failures because create_entities() is called from microRosTick() with no
parameters; fix by replacing the undefined topicName with a hardcoded topic
string (matching the ROS build pattern) inside create_entities() where
RCCHECK(rclc_publisher_init_best_effort(&sensorPublisher, &node,
ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg, RefSpeed), topicName)) is
invoked, ensuring the publisher is created with a concrete topic name and no
function signature changes to create_entities() or microRosTick().

Comment on lines +171 to +172
RCCHECK(rclc_executor_add_timer(&executor, &timer));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Guard ROS-only entities during executor registration and teardown.

Line 171/Line 321 add timer in all builds, while timer creation is ROS-only. Line 195/Line 196 finalize ROS-only entities even in ROS_DEBUG. This can force RCCHECK into error_loop() on disconnect. Also, errorPublisher is created (Line 115-Line 119) but never finalized.

🛠️ Suggested fix
-    RCCHECK(rclc_executor_add_timer(&executor, &timer));
+    `#ifdef` ROS
+    RCCHECK(rclc_executor_add_timer(&executor, &timer));
+    `#endif`
...
-    RCCHECK(rcl_publisher_fini(&fingerprintPublisher, &node));
-    RCCHECK(rcl_timer_fini(&timer));
+    `#ifdef` ROS
+    RCCHECK(rcl_publisher_fini(&fingerprintPublisher, &node));
+    RCCHECK(rcl_publisher_fini(&errorPublisher, &node));
+    RCCHECK(rcl_timer_fini(&timer));
+    `#endif`
...
-    RCCHECK(rclc_executor_add_timer(&executor, &timer));
+    `#ifdef` ROS
+    RCCHECK(rclc_executor_add_timer(&executor, &timer));
+    `#endif`

Also applies to: 195-199, 321-321

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/microRosFunctions.cpp` around lines 171 -
172, Guard registration and teardown of ROS-only entities so they only run when
the timer/publisher were actually created: wrap the call to
rclc_executor_add_timer(&executor, &timer) (and the corresponding
rcl_timer_fini, rcl_publisher_fini and rclc_executor_fini calls) in the same
ROS-only conditional used when creating the timer and errorPublisher, or track
an "initialized" flag for timer/errorPublisher and check it before finalizing;
ensure RCCHECK is not invoked on uninitialized objects (avoid calling RCCHECK on
rcl_* fns when the entities weren't created) and add a proper rcl_publisher_fini
for errorPublisher to finalize it when it was created to prevent RCCHECK from
hitting error_loop().

Comment on lines +15 to +20
float clock_freq = 125000000.0; // Default RP2040 system clock (125 MHz)
uint16_t wrap = clock_freq / frequency - 1;
pwm_set_wrap(slice, wrap);

uint16_t level = (wrap * duty_cycle) / 100.0;
pwm_set_chan_level(slice, channel, level);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Guard invalid frequency/duty_cycle before computing wrap and level.

Line 16 can divide by zero (frequency <= 0), and Lines 19-20 can program invalid levels when duty_cycle is outside [0, 100]. This can produce undefined PWM behavior.

🔧 Proposed fix
 void setPWM(uint pin, float frequency, float duty_cycle) {
+    if (frequency <= 0.0f) {
+        return;
+    }
+    if (duty_cycle < 0.0f) duty_cycle = 0.0f;
+    if (duty_cycle > 100.0f) duty_cycle = 100.0f;
+
     uint slice = pwm_gpio_to_slice_num(pin);
     uint channel = pwm_gpio_to_channel(pin);

     gpio_set_function(pin, GPIO_FUNC_PWM);

-    float clock_freq = 125000000.0; // Default RP2040 system clock (125 MHz)
-    uint16_t wrap = clock_freq / frequency - 1;
+    float clock_freq = 125000000.0f; // Default RP2040 system clock (125 MHz)
+    float wrap_f = (clock_freq / frequency) - 1.0f;
+    if (wrap_f < 0.0f) wrap_f = 0.0f;
+    if (wrap_f > 65535.0f) wrap_f = 65535.0f;
+    uint16_t wrap = static_cast<uint16_t>(wrap_f);
     pwm_set_wrap(slice, wrap);

-    uint16_t level = (wrap * duty_cycle) / 100.0;
+    uint16_t level = static_cast<uint16_t>((wrap * duty_cycle) / 100.0f);
     pwm_set_chan_level(slice, channel, level);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/PWMFunctions.cpp` around lines 15 - 20,
Validate inputs before computing wrap/level: ensure frequency > 0 (return or
error out if not) and compute raw_wrap = clock_freq / frequency, then convert to
an integer wrap = max(0, (int32_t)raw_wrap - 1) before calling
pwm_set_wrap(slice, wrap) to avoid negative/underflow values; clamp duty_cycle
into [0, 100], compute level = (wrap * clamped_duty_cycle) / 100.0 and ensure
level is bounded to [0, wrap] before calling pwm_set_chan_level(slice, channel,
level); reference the variables/functions frequency, duty_cycle, wrap, level,
slice, channel, pwm_set_wrap and pwm_set_chan_level when making the changes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
firmware/sensor_microcontroller/include/microRosFunctions.h (2)

52-57: ⚠️ Potential issue | 🔴 Critical

transmitMsg signature mismatch in ROS_DEBUG block.

The declaration void transmitMsg(RefSpeed omegaRef) has a single parameter, but the implementation and call sites use two parameters (RefDisplacement thetaRef, RefSpeed omegaRef). This will cause compilation errors when building with ROS_DEBUG defined.

Update the declaration to match:

-void transmitMsg(RefSpeed omegaRef);
+void transmitMsg(RefDisplacement thetaRef, RefSpeed omegaRef);

,

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/include/microRosFunctions.h` around lines 52
- 57, The prototype for transmitMsg is wrong: change the declaration of
transmitMsg to accept both parameters used by its implementation and
callers—RefDisplacement and RefSpeed—so the signature matches (e.g.,
transmitMsg(RefDisplacement thetaRef, RefSpeed omegaRef)). Update the
declaration in the header where transmitMsg is declared so it matches the
implementation and all call sites (ensure RefDisplacement is in scope or
forward-declared as needed).

37-42: ⚠️ Potential issue | 🔴 Critical

Callback declarations missing for ROS_DEBUG builds.

The subscription callback declarations (fan_subscription_callback, light_subscription_callback, lidar_subscription_callback) are guarded by #ifdef ROS, but the executor registration code in microRosFunctions.cpp (lines 161-170) references these callbacks unconditionally in both ROS and ROS_DEBUG builds. This will cause undefined reference errors when compiling with ROS_DEBUG.

Move these declarations outside the conditional block so they are visible in both build configurations.

,

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/include/microRosFunctions.h` around lines 37
- 42, The three subscription callback declarations (fan_subscription_callback,
light_subscription_callback, lidar_subscription_callback) must be made visible
in all build configs: move or change their declarations so they are not inside
an `#ifdef` ROS block in the header and are declared unconditionally with the same
signature (const void *msgin), because the executor registration code
unconditionally references these symbols; update the header to remove the `#ifdef`
guard around those prototypes so the linker sees them in ROS_DEBUG builds.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@firmware/sensor_microcontroller/include/microRosFunctions.h`:
- Around line 52-57: The prototype for transmitMsg is wrong: change the
declaration of transmitMsg to accept both parameters used by its implementation
and callers—RefDisplacement and RefSpeed—so the signature matches (e.g.,
transmitMsg(RefDisplacement thetaRef, RefSpeed omegaRef)). Update the
declaration in the header where transmitMsg is declared so it matches the
implementation and all call sites (ensure RefDisplacement is in scope or
forward-declared as needed).
- Around line 37-42: The three subscription callback declarations
(fan_subscription_callback, light_subscription_callback,
lidar_subscription_callback) must be made visible in all build configs: move or
change their declarations so they are not inside an `#ifdef` ROS block in the
header and are declared unconditionally with the same signature (const void
*msgin), because the executor registration code unconditionally references these
symbols; update the header to remove the `#ifdef` guard around those prototypes so
the linker sees them in ROS_DEBUG builds.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d5f25a and aeded99.

📒 Files selected for processing (1)
  • firmware/sensor_microcontroller/include/microRosFunctions.h

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
firmware/sensor_microcontroller/src/microRosFunctions.cpp (2)

138-144: ⚠️ Potential issue | 🔴 Critical

Fix undefined topicName in create_entities() ROS_DEBUG path.

Line 143 uses topicName, but create_entities() has no such variable in scope. This breaks ROS_DEBUG compilation.

🛠️ Suggested fix
 `#elif` ROS_DEBUG
   // create publisher
   RCCHECK(rclc_publisher_init_best_effort(
       &sensorPublisher, &node,
       ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg, RefSpeed),
-      topicName));
+      "ref_speed"));
 `#endif`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/microRosFunctions.cpp` around lines 138 -
144, In create_entities() the ROS_DEBUG path calls
rclc_publisher_init_best_effort with an undefined topicName; define or use the
correct topic string variable in scope (e.g., add a local const char* topicName
initialized to the same topic used elsewhere or replace topicName with the
existing topic variable) so sensorPublisher is initialized with a valid topic;
update the ROS_DEBUG block around rclc_publisher_init_best_effort
(&sensorPublisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg,
RefSpeed), topicName) accordingly.

175-175: ⚠️ Potential issue | 🔴 Critical

Guard timer/exclusive ROS entities consistently during add/fini.

Line 175 and Line 317 always add timer, but timer is only initialized in ROS builds. Also, Line 198/Line 199 teardown runs even when those entities were never created (ROS_DEBUG), and errorPublisher is never finalized.

This can force RCCHECK into error_loop() on disconnect/reconnect.

🛠️ Suggested fix
   RCCHECK(rclc_executor_add_subscription(&executor, &lidarSubscriber, &lidarMsg,
                                          &lidar_subscription_callback,
                                          ON_NEW_DATA));
-  RCCHECK(rclc_executor_add_timer(&executor, &timer));
+  `#ifdef` ROS
+  RCCHECK(rclc_executor_add_timer(&executor, &timer));
+  `#endif`
...
   RCCHECK(rcl_subscription_fini(&fanSubscriber, &node));
   RCCHECK(rcl_subscription_fini(&lidarSubscriber, &node));
   RCCHECK(rcl_subscription_fini(&lightSubscriber, &node));
   RCCHECK(rcl_publisher_fini(&sensorPublisher, &node));
-  RCCHECK(rcl_publisher_fini(&fingerprintPublisher, &node));
-  RCCHECK(rcl_timer_fini(&timer));
+  `#ifdef` ROS
+  RCCHECK(rcl_publisher_fini(&fingerprintPublisher, &node));
+  RCCHECK(rcl_publisher_fini(&errorPublisher, &node));
+  RCCHECK(rcl_timer_fini(&timer));
+  `#endif`
   RCCHECK(rclc_executor_fini(&executor));
...
-  RCCHECK(rclc_executor_add_timer(&executor, &timer));
+  `#ifdef` ROS
+  RCCHECK(rclc_executor_add_timer(&executor, &timer));
+  `#endif`

Also applies to: 194-200, 317-317

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/microRosFunctions.cpp` at line 175, The
code unconditionally calls RCCHECK(rclc_executor_add_timer(&executor, &timer))
and always runs teardown paths even when ROS entities (timer, errorPublisher)
were never created; add guards that track creation (e.g., bool timer_created and
error_publisher_created) or wrap the add/fini calls with the same ROS build
conditional used for initializing them so rclc_executor_add_timer and
rcl_publisher_fini are only invoked if timer and errorPublisher were actually
initialized; update the teardown block (the code around symbols timer, executor,
errorPublisher, and their RCCHECK/fini calls) to check those flags (or the ROS
`#ifdef`) before calling RCCHECK or rcl_*_fini to avoid hitting error_loop() on
disconnect/reconnect.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@firmware/sensor_microcontroller/src/microRosFunctions.cpp`:
- Around line 264-275: microRosSetup() currently initializes sensorPublisher and
fingerprintPublisher but never initializes errorPublisher, while publishError()
always uses errorPublisher; update microRosSetup() to initialize errorPublisher
(use rclc_publisher_init_best_effort or rclc_publisher_init_default consistent
with the other publishers) with the node, the correct message type support
(ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg, Error) or the actual
Error message type used by publishError()), and errorTopicName so that
publishError() has a valid publisher to send error messages through.

---

Duplicate comments:
In `@firmware/sensor_microcontroller/src/microRosFunctions.cpp`:
- Around line 138-144: In create_entities() the ROS_DEBUG path calls
rclc_publisher_init_best_effort with an undefined topicName; define or use the
correct topic string variable in scope (e.g., add a local const char* topicName
initialized to the same topic used elsewhere or replace topicName with the
existing topic variable) so sensorPublisher is initialized with a valid topic;
update the ROS_DEBUG block around rclc_publisher_init_best_effort
(&sensorPublisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg,
RefSpeed), topicName) accordingly.
- Line 175: The code unconditionally calls
RCCHECK(rclc_executor_add_timer(&executor, &timer)) and always runs teardown
paths even when ROS entities (timer, errorPublisher) were never created; add
guards that track creation (e.g., bool timer_created and
error_publisher_created) or wrap the add/fini calls with the same ROS build
conditional used for initializing them so rclc_executor_add_timer and
rcl_publisher_fini are only invoked if timer and errorPublisher were actually
initialized; update the teardown block (the code around symbols timer, executor,
errorPublisher, and their RCCHECK/fini calls) to check those flags (or the ROS
`#ifdef`) before calling RCCHECK or rcl_*_fini to avoid hitting error_loop() on
disconnect/reconnect.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 90a0c115-ce4f-4a65-b191-a9fdf69ad84e

📥 Commits

Reviewing files that changed from the base of the PR and between aeded99 and 7f74bda.

📒 Files selected for processing (2)
  • firmware/sensor_microcontroller/src/microRosFunctions.cpp
  • frontend/src/renderer/src/types/ros-msgs.gen.ts

Comment on lines +264 to +275
#ifdef ROS
// create publisher
RCCHECK(rclc_publisher_init_best_effort(
&sensorPublisher, &node,
ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg, Sensors),
sensorTopicName));
// Create fingerprint publisher
RCCHECK(rclc_publisher_init_default(
&fingerprintPublisher, &node,
ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg, Fingerprint),
fingerprintTopicName));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Initialize errorPublisher in ROS microRosSetup() to match publishError().

publishError() (Line 335-344) always publishes through errorPublisher, but ROS microRosSetup() (Line 264-275) never initializes that publisher. Result: error messages are dropped/fail silently.

🛠️ Suggested fix
 `#ifdef` ROS
   // create publisher
   RCCHECK(rclc_publisher_init_best_effort(
       &sensorPublisher, &node,
       ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg, Sensors),
       sensorTopicName));
   // Create fingerprint publisher
   RCCHECK(rclc_publisher_init_default(
       &fingerprintPublisher, &node,
       ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg, Fingerprint),
       fingerprintTopicName));
+  // Create error publisher
+  RCCHECK(rclc_publisher_init_default(
+      &errorPublisher, &node,
+      ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg, SensorError),
+      "sensor_error"));

Also applies to: 335-344

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/sensor_microcontroller/src/microRosFunctions.cpp` around lines 264 -
275, microRosSetup() currently initializes sensorPublisher and
fingerprintPublisher but never initializes errorPublisher, while publishError()
always uses errorPublisher; update microRosSetup() to initialize errorPublisher
(use rclc_publisher_init_best_effort or rclc_publisher_init_default consistent
with the other publishers) with the node, the correct message type support
(ROSIDL_GET_MSG_TYPE_SUPPORT(autogiro_interfaces, msg, Error) or the actual
Error message type used by publishError()), and errorTopicName so that
publishError() has a valid publisher to send error messages through.

@rossnoah
rossnoah merged commit e07d528 into main Mar 4, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant