Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

APA102 cpp implementation #108

Merged
merged 34 commits into from
May 10, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2d4f175
Merge pull request #99 from husarion/ros1-bump-package-verstion
KmakD Mar 23, 2023
1e4f9c6
Use CPP for APA102
Kotochleb Apr 3, 2023
7134601
Add color correction
Kotochleb Apr 4, 2023
2dd1c0e
Utilise gpiod
Kotochleb Apr 5, 2023
8e5956d
Remove ::
Kotochleb Apr 5, 2023
ca2abc8
Fix lights after tests
Kotochleb Apr 5, 2023
aa86625
Fix default pin number
Kotochleb Apr 5, 2023
666834c
Add set brightness service
Kotochleb Apr 6, 2023
639ea52
Invert LED_SBC_SEL pin
Kotochleb Apr 7, 2023
c96a5a5
Change SPI mode
Kotochleb Apr 11, 2023
7353325
Change clock polarity
Kotochleb Apr 12, 2023
f9ef2c8
Reduce number of ROS lgger instances
Kotochleb Apr 18, 2023
83fe3b8
Celan up code, update to libgpiod 2.0
Kotochleb Apr 18, 2023
0493f37
Update panther_lights/include/panther_lights/apa102.hpp
Kotochleb Apr 21, 2023
809ddc3
Update panther_lights/src/lights_driver_node.cpp
Kotochleb Apr 21, 2023
be28a69
Update panther_lights/src/lights_driver_node.cpp
Kotochleb Apr 21, 2023
e853280
Update panther_lights/include/panther_lights/apa102.hpp
Kotochleb Apr 21, 2023
54947be
Review changes
Kotochleb Apr 21, 2023
829f617
Merge branch 'ros1-apa102-cpp' of github.com:husarion/panther_ros int…
Kotochleb Apr 21, 2023
4175b7a
Update panther_lights/src/apa102.cpp
Kotochleb Apr 21, 2023
c212ed2
Review cleanup
Kotochleb Apr 21, 2023
786a637
Add build dependencies
Kotochleb Apr 21, 2023
2a192bb
FIx launchfile
Kotochleb Apr 21, 2023
f478d0e
Update panther_lights/CMakeLists.txt
Kotochleb May 8, 2023
5907453
Update panther_lights/CMakeLists.txt
Kotochleb May 8, 2023
0ed663d
Update panther_lights/src/apa102.cpp
Kotochleb May 8, 2023
2468f11
Update panther_lights/src/driver_node.cpp
Kotochleb May 8, 2023
685508f
Update panther_lights/src/driver_node.cpp
Kotochleb May 8, 2023
0d19458
Review changes
Kotochleb May 8, 2023
850784d
Merge branch 'ros1-apa102-cpp' of github.com:husarion/panther_ros int…
Kotochleb May 8, 2023
fee608f
Fix inlude order
Kotochleb May 8, 2023
711d85c
Fix references and APA102 constructor
Kotochleb May 8, 2023
febc8b0
Add build dependencies for libgpiod v2
Kotochleb May 9, 2023
4ca10ea
Bring back libgpiod v1
Kotochleb May 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 49 additions & 7 deletions panther_lights/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,57 @@
cmake_minimum_required(VERSION 3.0.2)
cmake_minimum_required(VERSION 3.16.3)
project(panther_lights)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_library(LIBGPIOD libgpiod)
if(NOT LIBGPIOD)
Kotochleb marked this conversation as resolved.
Show resolved Hide resolved
message(WARNING "libgpiod not found. Building from source")
include(ExternalProject)

ExternalProject_Add(libgpiod
GIT_REPOSITORY git://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git
GIT_TAG v2.0.1
PREFIX ${CATKIN_DEVEL_PREFIX}
CONFIGURE_COMMAND <SOURCE_DIR>/autogen.sh --prefix=<INSTALL_DIR> --enable-tools=no --enable-bindings-cxx
BUILD_COMMAND make -j ${N}
INSTALL_COMMAND make install INSTALL_PREFIX=${CATKIN_DEVEL_PREFIX}/lib/libgpiod
BUILD_IN_SOURCE 1
)
endif()

find_package(catkin REQUIRED COMPONENTS
panther_msgs
rospy
sensor_msgs
std_msgs
std_srvs
image_transport
panther_msgs
roscpp
rospy
sensor_msgs
std_msgs
std_srvs
Kotochleb marked this conversation as resolved.
Show resolved Hide resolved
)

catkin_package(
INCLUDE_DIRS include
LIBRARIES libgpiod
CATKIN_DEPENDS panther_msgs roscpp
)

catkin_package()
include_directories(
include
${catkin_INCLUDE_DIRS}
)

add_executable(driver_node
src/main.cpp
src/driver_node.cpp
src/apa102.cpp
)

add_dependencies(driver_node libgpiod ${catkin_EXPORTED_TARGETS})
target_link_libraries(driver_node
${CATKIN_DEVEL_PREFIX}/lib/libgpiodcxx.so
${catkin_LIBRARIES}
)

install(DIRECTORY
config
Expand Down
2 changes: 1 addition & 1 deletion panther_lights/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ This node is responsible for processing animations and publishing frames to be d
- `~test` [*bool*, default: **false**]: enables testing mode, enabling extra functionalities.
- `~user_animations` [*list*, default: **None**]: optional list of animations defined by the user.

### driver_node.py
### driver_node

This node is responsible for displaying frames on the Husarion Panther robot LED panels.

Expand Down
36 changes: 36 additions & 0 deletions panther_lights/include/panther_lights/apa102.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef PANTHER_LIGHTS_APA102_HPP_
#define PANTHER_LIGHTS_APA102_HPP_

#include <cstdint>
#include <string>
#include <vector>

namespace apa_102
KmakD marked this conversation as resolved.
Show resolved Hide resolved
{

class APA102
{
public:
APA102(const std::string device, const std::uint32_t speed = 800000, const bool cs_high = false);
~APA102();

void set_global_brightness(const std::uint8_t brightness);
Kotochleb marked this conversation as resolved.
Show resolved Hide resolved
void set_global_brightness(const double brightness);
void set_panel(const std::vector<std::uint8_t> & frame) const;

private:
const int fd_;
const std::string device_;
const std::uint8_t bits_ = 8;
const std::uint32_t speed_;
std::uint16_t global_brightness_;

// color correction constants
const std::uint16_t corr_red_ = 255.0;
const std::uint16_t corr_green_ = 200.0;
const std::uint16_t corr_blue_ = 62.0;
};

} // namespace apa_102

#endif // PANTHER_LIGHTS_APA102_HPP_
56 changes: 56 additions & 0 deletions panther_lights/include/panther_lights/driver_node.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#ifndef PANTHER_LIGHTS_DRIVER_NODE_HPP_
#define PANTHER_LIGHTS_DRIVER_NODE_HPP_

#include <panther_lights/apa102.hpp>

#include <fstream>
#include <memory>
#include <vector>

#include <gpiod.hpp>
#include <ros/ros.h>

#include <image_transport/image_transport.h>

#include <panther_msgs/SetLEDBrightness.h>

namespace panther_lights
{

class DriverNode
{
public:
DriverNode(
const std::shared_ptr<ros::NodeHandle> ph, std::shared_ptr<ros::NodeHandle> nh,
const std::shared_ptr<image_transport::ImageTransport> it);
~DriverNode();

private:
int num_led_;
double frame_timeout_;
bool panels_initialised_ = false;
std::string node_name_;

apa_102::APA102 front_panel_;
apa_102::APA102 rear_panel_;

ros::Time front_panel_ts_;
ros::Time rear_panel_ts_;
std::shared_ptr<ros::NodeHandle> ph_;
std::shared_ptr<ros::NodeHandle> nh_;
std::shared_ptr<image_transport::ImageTransport> it_;
ros::ServiceServer set_brightness_server_;
image_transport::Subscriber rear_light_sub_;
image_transport::Subscriber front_light_sub_;

void frame_cb(
const sensor_msgs::Image::ConstPtr & msg, const apa_102::APA102 & panel,
const ros::Time & last_time, const std::string panel_name);
void set_pin_value(const gpiod::line::value value) const;
bool set_brightness_cb(
panther_msgs::SetLEDBrightness::Request & req, panther_msgs::SetLEDBrightness::Response & res);
};

} // namespace panther_lights

#endif // PANTHER_LIGHTS_DRIVER_NODE_HPP_
2 changes: 1 addition & 1 deletion panther_lights/launch/lights.launch
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<arg name="test" default="false" />
<arg name="user_animations_file" default="" />

<node pkg="panther_lights" type="driver_node.py" name="lights_driver_node"
<node pkg="panther_lights" type="driver_node" name="lights_driver_node"
required="true" output="screen" />

<node pkg="panther_lights" type="controller_node.py" name="lights_controller_node"
Expand Down
4 changes: 2 additions & 2 deletions panther_lights/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@

<buildtool_depend>catkin</buildtool_depend>

<depend>image_transport</depend>
<depend>panther_msgs</depend>
<depend>roscpp</depend>
<depend>rospy</depend>
<depend>sensor_msgs</depend>
<depend>std_msgs</depend>
<depend>std_srvs</depend>

<!-- Python dependencies -->
<depend>python-rpi.gpio-pip</depend>
<depend>python3-apa102-pi-pip</depend>
<depend>python3-pil</depend>

</package>
99 changes: 99 additions & 0 deletions panther_lights/src/apa102.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#include <panther_lights/apa102.hpp>

#include <fcntl.h>
#include <unistd.h>
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>
Kotochleb marked this conversation as resolved.
Show resolved Hide resolved

#include <cmath>
#include <cstdint>
#include <fstream>
#include <stdexcept>
#include <string>
#include <vector>
KmakD marked this conversation as resolved.
Show resolved Hide resolved

namespace apa_102
KmakD marked this conversation as resolved.
Show resolved Hide resolved
{
APA102::APA102(const std::string device, const std::uint32_t speed, const bool cs_high)
: device_(device), speed_(speed), fd_(open(device_.c_str(), O_RDWR))
{
if (fd_ < 0) {
throw std::ios_base::failure(std::string("Failed to open ") + device_);
}

static std::uint8_t mode = cs_high ? SPI_MODE_3 : SPI_MODE_3 | SPI_CS_HIGH;
if (ioctl(fd_, SPI_IOC_WR_MODE32, &mode) == -1) {
close(fd_);
throw std::ios_base::failure(std::string("Failed to set mode for ") + device_);
}

if (ioctl(fd_, SPI_IOC_WR_BITS_PER_WORD, &bits_) == -1) {
close(fd_);
throw std::ios_base::failure(std::string("Can't set bits per word for ") + device_);
}

if (ioctl(fd_, SPI_IOC_WR_MAX_SPEED_HZ, &speed_) == -1) {
close(fd_);
throw std::ios_base::failure(std::string("Can't set speed for ") + device_);
}
}

APA102::~APA102() { close(fd_); }

void APA102::set_global_brightness(const double brightness)
{
std::uint8_t val = brightness > 0.0f ? ceil(brightness * 31.0f) : 0;
set_global_brightness(val);
}

void APA102::set_global_brightness(const std::uint8_t brightness)
{
// clamp values to be at max 31
global_brightness_ = std::uint16_t(brightness) & 0x1F;
}

void APA102::set_panel(const std::vector<std::uint8_t> & frame) const
{
if (frame.size() % 4 != 0) {
throw std::runtime_error("Incorrect number of bytes to transfer to LEDs");
}
// init buffer with start and end frames
std::size_t buffer_size = (4 * sizeof(std::uint8_t)) + frame.size() + (4 * sizeof(std::uint8_t));
KmakD marked this conversation as resolved.
Show resolved Hide resolved
std::uint8_t * buffer = new std::uint8_t[buffer_size];

// init start and end frames
for (std::size_t i = 0; i < 4; i++) {
buffer[i] = 0x00;
buffer[buffer_size - i - 1] = 0xFF;
}

// copy frame from vector to sending buffer
for (std::size_t i = 0; i < frame.size() / 4; i++) {
// padding
std::size_t pad = i * 4;
// header with brightness
std::uint8_t brightness = (std::uint16_t(frame[pad + 3]) * global_brightness_) / 255;
buffer[4 + pad] = 0xE0 | brightness;
KmakD marked this conversation as resolved.
Show resolved Hide resolved
// convert rgb to bgr with collor correction
buffer[4 + pad + 1] = std::uint8_t((std::uint16_t(frame[pad + 2]) * corr_blue_) / 255);
buffer[4 + pad + 2] = std::uint8_t((std::uint16_t(frame[pad + 1]) * corr_green_) / 255);
buffer[4 + pad + 3] = std::uint8_t((std::uint16_t(frame[pad + 0]) * corr_red_) / 255);
}

struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long long)buffer,
.rx_buf = 0,
.len = (unsigned int)buffer_size,
.speed_hz = speed_,
.delay_usecs = 0,
.bits_per_word = 8,
};

int ret = ioctl(fd_, SPI_IOC_MESSAGE(1), &tr);
delete[] buffer;

if (ret < 1) {
throw std::ios_base::failure(std::string("Failed to send data over SPI ") + device_);
}
}
} // namespace apa_102
Loading