|
| 1 | +/* |
| 2 | + * Copyright (c) 2024 Arduino |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: MPL-2.0 |
| 5 | + * This Source Code Form is subject to the terms of the Mozilla Public |
| 6 | + * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 7 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 8 | + */ |
| 9 | + |
| 10 | +/************************************************************************************** |
| 11 | + * INCLUDE |
| 12 | + **************************************************************************************/ |
| 13 | + |
| 14 | +#include <Arduino_Alvik.h> |
| 15 | + |
| 16 | +#include <micro_ros_arduino.h> |
| 17 | + |
| 18 | +#include <stdio.h> |
| 19 | +#include <rcl/rcl.h> |
| 20 | +#include <rcl/error_handling.h> |
| 21 | +#include <rclc/rclc.h> |
| 22 | +#include <rclc/executor.h> |
| 23 | + |
| 24 | +#include <std_msgs/msg/int32.h> |
| 25 | + |
| 26 | +#include <nav_msgs/msg/odometry.h> |
| 27 | +#include <geometry_msgs/msg/twist.h> |
| 28 | + |
| 29 | +#include <micro_ros_utilities/string_utilities.h> |
| 30 | + |
| 31 | +#include "wifi_secrets.h" |
| 32 | +#include "micro_ros_config.h" |
| 33 | + |
| 34 | +/************************************************************************************** |
| 35 | + * GLOBAL VARIABLES |
| 36 | + **************************************************************************************/ |
| 37 | + |
| 38 | +static rcl_subscription_t cmd_vel_sub; |
| 39 | +static geometry_msgs__msg__Twist cmd_vel_msg; |
| 40 | + |
| 41 | +static rcl_timer_t odom_timer; |
| 42 | +static rcl_publisher_t odom_pub; |
| 43 | +static nav_msgs__msg__Odometry odom_msg; |
| 44 | + |
| 45 | +static rclc_support_t support; |
| 46 | +static rcl_allocator_t allocator = rcl_get_default_allocator(); |
| 47 | +static rclc_executor_t executor = rclc_executor_get_zero_initialized_executor(); |
| 48 | +static rcl_node_t node = rcl_get_zero_initialized_node(); |
| 49 | + |
| 50 | +static Arduino_Alvik alvik; |
| 51 | + |
| 52 | +/************************************************************************************** |
| 53 | + * LOCAL FUNCTIONS |
| 54 | + **************************************************************************************/ |
| 55 | + |
| 56 | +void error_loop(char const * fmt, ...) |
| 57 | +{ |
| 58 | + va_list args; |
| 59 | + va_start(args, fmt); |
| 60 | + |
| 61 | + char msg[64] = {0}; |
| 62 | + vsnprintf(msg, sizeof(msg), fmt, args); |
| 63 | + Serial.println(msg); |
| 64 | + |
| 65 | + va_end(args); |
| 66 | + |
| 67 | + for(;;) |
| 68 | + { |
| 69 | + digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); |
| 70 | + delay(100); |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +void euler_to_quat(float const x, float const y, float const z, double * q) |
| 75 | +{ |
| 76 | + float c1 = cos((y*3.14f/180.f)/2.f); |
| 77 | + float c2 = cos((z*3.14f/180.f)/2.f); |
| 78 | + float c3 = cos((x*3.14f/180.f)/2.f); |
| 79 | + |
| 80 | + float s1 = sin((y*3.14f/180.f)/2.f); |
| 81 | + float s2 = sin((z*3.14f/180.f)/2.f); |
| 82 | + float s3 = sin((x*3.14f/180.f)/2.f); |
| 83 | + |
| 84 | + q[0] = c1 * c2 * c3 - s1 * s2 * s3; |
| 85 | + q[1] = s1 * s2 * c3 + c1 * c2 * s3; |
| 86 | + q[2] = s1 * c2 * c3 + c1 * s2 * s3; |
| 87 | + q[3] = c1 * s2 * c3 - s1 * c2 * s3; |
| 88 | +} |
| 89 | + |
| 90 | +void cmd_vel_callback(const void *msgin) |
| 91 | +{ |
| 92 | + const geometry_msgs__msg__Twist * msg = (const geometry_msgs__msg__Twist *)msgin; |
| 93 | + alvik.drive(msg->linear.x, msg->angular.z, M_S, RAD_S); |
| 94 | +} |
| 95 | + |
| 96 | +void odom_timer_callback(rcl_timer_t * timer, int64_t last_call_time) |
| 97 | +{ |
| 98 | + RCLC_UNUSED(last_call_time); |
| 99 | + if (timer != NULL) |
| 100 | + { |
| 101 | + struct timespec ts; |
| 102 | + clock_gettime(CLOCK_REALTIME, &ts); |
| 103 | + odom_msg.header.stamp.sec = ts.tv_sec; |
| 104 | + odom_msg.header.stamp.nanosec = ts.tv_nsec; |
| 105 | + |
| 106 | + odom_msg.header.frame_id = micro_ros_string_utilities_init("odom"); |
| 107 | + |
| 108 | + float x = 0.f, y = 0.f, theta = 0.f; |
| 109 | + alvik.get_pose(x, y, theta, M, RAD); |
| 110 | + |
| 111 | + odom_msg.pose.pose.position.x = x; |
| 112 | + odom_msg.pose.pose.position.y = y; |
| 113 | + odom_msg.pose.pose.position.z = 0.f; |
| 114 | + |
| 115 | + double q[4] = {0.}; |
| 116 | + euler_to_quat(0., 0., theta, q); |
| 117 | + odom_msg.pose.pose.orientation.x = q[1]; |
| 118 | + odom_msg.pose.pose.orientation.y = q[2]; |
| 119 | + odom_msg.pose.pose.orientation.z = q[3]; |
| 120 | + odom_msg.pose.pose.orientation.w = q[0]; |
| 121 | + |
| 122 | + float linear = 0.f, angular = 0.f; |
| 123 | + alvik.get_drive_speed(linear, angular, M_S, RAD_S); |
| 124 | + |
| 125 | + odom_msg.twist.twist.linear.x = linear; |
| 126 | + odom_msg.twist.twist.linear.y = 0.f; |
| 127 | + odom_msg.twist.twist.angular.z = angular; |
| 128 | + |
| 129 | + if (rcl_ret_t const rc = rcl_publish(&odom_pub, &odom_msg, NULL); rc != RCL_RET_OK) |
| 130 | + error_loop("odom_timer_callback::rcl_publish failed with %d", rc); |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +/************************************************************************************** |
| 135 | + * SETUP/LOOP |
| 136 | + **************************************************************************************/ |
| 137 | + |
| 138 | +void setup() |
| 139 | +{ |
| 140 | + Serial.begin(); |
| 141 | + for (auto const start = millis(); !Serial && (millis() - start) < 1000; ) { } |
| 142 | + |
| 143 | + set_microros_wifi_transports(SECRET_WIFI_SSID, SECRET_WIFI_PASS, MICRO_ROS_AGENT_IP, MICRO_ROS_AGENT_PORT); |
| 144 | + |
| 145 | + pinMode(LED_BUILTIN, OUTPUT); |
| 146 | + digitalWrite(LED_BUILTIN, HIGH); |
| 147 | + |
| 148 | + alvik.begin(); |
| 149 | + |
| 150 | + if (rcl_ret_t const rc = rclc_support_init(&support, 0, NULL, &allocator); rc != RCL_RET_OK) |
| 151 | + error_loop("rclc_support_init failed with %d", rc); |
| 152 | + |
| 153 | + if (rcl_ret_t const rc = rclc_node_init_default(&node, "alvik", "", &support); rc != RCL_RET_OK) |
| 154 | + error_loop("rclc_node_init_default failed with %d", rc); |
| 155 | + |
| 156 | + if (rcl_ret_t const rc = rclc_subscription_init_default(&cmd_vel_sub, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(geometry_msgs, msg, Twist), "/cmd_vel"); rc != RCL_RET_OK) |
| 157 | + error_loop("rclc_subscription_init_default failed with %d", rc); |
| 158 | + |
| 159 | + if (rcl_ret_t const rc = rclc_timer_init_default(&odom_timer, &support, RCL_MS_TO_NS(50), odom_timer_callback); rc != RCL_RET_OK) |
| 160 | + error_loop("rclc_timer_init_default(odom_timer, ...) failed with %d", rc); |
| 161 | + |
| 162 | + if (rcl_ret_t const rc = rclc_publisher_init_default(&odom_pub, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(nav_msgs, msg, Odometry), "/odom"); rc != RCL_RET_OK) |
| 163 | + error_loop("rclc_publisher_init_default(odom_pub, ...) failed with %d", rc); |
| 164 | + |
| 165 | + if (rcl_ret_t const rc = rclc_executor_init(&executor, &support.context, 2, &allocator); rc != RCL_RET_OK) |
| 166 | + error_loop("rclc_executor_init failed with %d", rc); |
| 167 | + |
| 168 | + if (rcl_ret_t const rc = rclc_executor_add_subscription(&executor, &cmd_vel_sub, &cmd_vel_msg, &cmd_vel_callback, ON_NEW_DATA); rc != RCL_RET_OK) |
| 169 | + error_loop("rclc_executor_add_subscription(..., cmd_vel_sub, ...) failed with %d", rc); |
| 170 | + |
| 171 | + if (rcl_ret_t const rc = rclc_executor_add_timer(&executor, &odom_timer); rc != RCL_RET_OK) |
| 172 | + error_loop("rclc_executor_add_timer(..., odom_timer, ...) failed with %d", rc); |
| 173 | + |
| 174 | + Serial.println("alvik_ros2_firmware setup complete."); |
| 175 | +} |
| 176 | + |
| 177 | +void loop() |
| 178 | +{ |
| 179 | + rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100)); |
| 180 | +} |
0 commit comments