led.cpp
Go to the documentation of this file.
1 /*
2  * High level control for the LED strip
3  * Indicate flight events with the LED strip
4  * Copyright (C) 2019 Copter Express Technologies
5  *
6  * Author: Oleg Kalachev <okalachev@gmail.com>
7  *
8  * Distributed under MIT License (available at https://opensource.org/licenses/MIT).
9  * The above copyright notice and this permission notice shall be included in all
10  * copies or substantial portions of the Software.
11  */
12 
13 #include <ros/ros.h>
14 #include <string>
15 #include <boost/algorithm/string.hpp>
16 
17 #include <clover/SetLEDEffect.h>
18 #include <led_msgs/SetLEDs.h>
19 #include <led_msgs/LEDState.h>
20 #include <led_msgs/LEDStateArray.h>
21 
22 #include <sensor_msgs/BatteryState.h>
23 #include <mavros_msgs/State.h>
24 #include <rosgraph_msgs/Log.h>
25 
26 clover::SetLEDEffect::Request current_effect;
33 led_msgs::SetLEDs set_leds;
34 led_msgs::LEDStateArray state, start_state;
36 mavros_msgs::State mavros_state;
37 int counter;
38 
40 {
41  bool res = set_leds_srv.call(set_leds);
42  if (!res) {
43  ROS_WARN_THROTTLE(5, "Error calling set_leds service");
44  } else if (!set_leds.response.success) {
45  ROS_WARN_THROTTLE(5, "Calling set_leds failed: %s", set_leds.response.message.c_str());
46  }
47 }
48 
49 void rainbow(uint8_t n, uint8_t& r, uint8_t& g, uint8_t& b)
50 {
51  if (n < 255 / 3) {
52  r = n * 3;
53  g = 255 - n * 3;
54  b = 0;
55  } else if (n < 255 / 3 * 2) {
56  n -= 255 / 3;
57  r = 255 - n * 3;
58  g = 0;
59  b = n * 3;
60  } else {
61  n -= 255 / 3 * 2;
62  r = 0;
63  g = n * 3;
64  b = 255 - n * 3;
65  }
66 }
67 
68 void fill(uint8_t r, uint8_t g, uint8_t b)
69 {
70  set_leds.request.leds.resize(led_count);
71  for (int i = 0; i < led_count; i++) {
72  set_leds.request.leds[i].index = i;
73  set_leds.request.leds[i].r = r;
74  set_leds.request.leds[i].g = g;
75  set_leds.request.leds[i].b = b;
76  }
77  callSetLeds();
78 }
79 
80 void proceed(const ros::TimerEvent& event)
81 {
82  counter++;
83  uint8_t r, g, b;
84  set_leds.request.leds.clear();
85  set_leds.request.leds.resize(led_count);
86 
87  if (current_effect.effect == "blink" || current_effect.effect == "blink_fast") {
89  // toggle all leds
90  if (blink_state) {
92  } else {
93  fill(0, 0, 0);
94  }
95 
96  } else if (current_effect.effect == "fade") {
97  // fade all leds from starting state
98  double passed = std::min((event.current_real - start_time).toSec() / fade_period, 1.0);
99  double one_minus_passed = 1 - passed;
100  for (int i = 0; i < led_count; i++) {
101  set_leds.request.leds[i].index = i;
102  set_leds.request.leds[i].r = one_minus_passed * start_state.leds[i].r + passed * current_effect.r;
103  set_leds.request.leds[i].g = one_minus_passed * start_state.leds[i].g + passed * current_effect.g;
104  set_leds.request.leds[i].b = one_minus_passed * start_state.leds[i].b + passed * current_effect.b;
105  }
106  callSetLeds();
107  if (passed >= 1.0) {
108  // fade finished
109  timer.stop();
110  }
111 
112  } else if (current_effect.effect == "wipe") {
113  set_leds.request.leds.resize(1);
114  set_leds.request.leds[0].index = counter - 1;
115  set_leds.request.leds[0].r = current_effect.r;
116  set_leds.request.leds[0].g = current_effect.g;
117  set_leds.request.leds[0].b = current_effect.b;
118  callSetLeds();
119  if (counter == led_count) {
120  // wipe finished
121  timer.stop();
122  }
123 
124  } else if (current_effect.effect == "rainbow_fill") {
125  rainbow(counter % 255, r, g, b);
126  for (int i = 0; i < led_count; i++) {
127  set_leds.request.leds[i].index = i;
128  set_leds.request.leds[i].r = r;
129  set_leds.request.leds[i].g = g;
130  set_leds.request.leds[i].b = b;
131  }
132  callSetLeds();
133 
134  } else if (current_effect.effect == "rainbow") {
135  for (int i = 0; i < led_count; i++) {
136  int pos = (int)round(counter + (255.0 * i / led_count)) % 255;
137  rainbow(pos % 255, r, g, b);
138  set_leds.request.leds[i].index = i;
139  set_leds.request.leds[i].r = r;
140  set_leds.request.leds[i].g = g;
141  set_leds.request.leds[i].b = b;
142  }
143  callSetLeds();
144  }
145 }
146 
147 bool setEffect(clover::SetLEDEffect::Request& req, clover::SetLEDEffect::Response& res)
148 {
149  res.success = true;
150 
151  if (req.effect == "") {
152  req.effect = "fill";
153  }
154 
155  if (req.effect != "flash" && req.effect != "fill" && current_effect.effect == req.effect &&
156  current_effect.r == req.r && current_effect.g == req.g && current_effect.b == req.b) {
157  res.message = "Effect already set, skip";
158  return true;
159  }
160 
161  if (req.effect == "fill") {
162  fill(req.r, req.g, req.b);
163 
164  } else if (req.effect == "blink") {
165  timer.setPeriod(ros::Duration(1 / blink_rate), true);
166  timer.start();
167 
168  } else if (req.effect == "blink_fast") {
169  timer.setPeriod(ros::Duration(1 / blink_fast_rate), true);
170  timer.start();
171 
172  } else if (req.effect == "fade") {
173  timer.setPeriod(ros::Duration(0.05), true);
174  timer.start();
175 
176  } else if (req.effect == "wipe") {
178  timer.start();
179 
180  } else if (req.effect == "flash") {
181  ros::Duration delay(flash_delay);
182  fill(0, 0, 0);
183  delay.sleep();
184  fill(req.r, req.g, req.b);
185  delay.sleep();
186  fill(0, 0, 0);
187  delay.sleep();
188  fill(req.r, req.g, req.b);
189  delay.sleep();
190  fill(0, 0, 0);
191  delay.sleep();
192  if (current_effect.effect == "fill"||
193  current_effect.effect == "fade" ||
194  current_effect.effect == "wipe") {
195  // restore previous filling
196  for (int i = 0; i < led_count; i++) {
198  }
199  callSetLeds();
200  }
201  return true; // this effect happens only once
202 
203  } else if (req.effect == "rainbow_fill") {
204  timer.setPeriod(ros::Duration(rainbow_period / 255), true);
205  timer.start();
206 
207  } else if (req.effect == "rainbow") {
208  timer.setPeriod(ros::Duration(rainbow_period / 255), true);
209  timer.start();
210 
211  } else {
212  res.message = "Unknown effect: " + req.effect + ". Available effects are fill, fade, wipe, blink, blink_fast, flash, rainbow, rainbow_fill.";
213  ROS_ERROR("%s", res.message.c_str());
214  res.success = false;
215  return true;
216  }
217 
218  // set current effect
219  current_effect = req;
220  counter = 0;
221  start_state = state;
222  start_time = ros::Time::now();
223 
224  return true;
225 }
226 
227 void handleState(const led_msgs::LEDStateArray& msg)
228 {
229  state = msg;
230  led_count = state.leds.size();
231 }
232 
233 void notify(const std::string& event)
234 {
235  if (ros::param::has("~notify/" + event + "/effect") ||
236  ros::param::has("~notify/" + event + "/r") ||
237  ros::param::has("~notify/" + event + "/g") ||
238  ros::param::has("~notify/" + event + "/b")) {
239  ROS_INFO_THROTTLE(5, "led: notify %s", event.c_str());
240  clover::SetLEDEffect effect;
241  effect.request.effect = ros::param::param("~notify/" + event + "/effect", std::string(""));
242  effect.request.r = ros::param::param("~notify/" + event + "/r", 0);
243  effect.request.g = ros::param::param("~notify/" + event + "/g", 0);
244  effect.request.b = ros::param::param("~notify/" + event + "/b", 0);
245  setEffect(effect.request, effect.response);
246  }
247 }
248 
249 void handleMavrosState(const mavros_msgs::State& msg)
250 {
251  if (msg.connected && !mavros_state.connected) {
252  notify("connected");
253  } else if (!msg.connected && mavros_state.connected) {
254  notify("disconnected");
255  } else if (msg.armed && !mavros_state.armed) {
256  notify("armed");
257  } else if (!msg.armed && mavros_state.armed) {
258  notify("disarmed");
259  } else if (msg.mode != mavros_state.mode) {
260  // mode changed
261  std::string mode = boost::algorithm::to_lower_copy(msg.mode);
262  if (mode.find(".") != std::string::npos) {
263  // remove the part before "."
264  mode = mode.substr(mode.find(".") + 1);
265  }
266  std::string err;
267  if (ros::names::validate(mode, err)) {
268  notify(mode);
269  }
270  }
271  mavros_state = msg;
272 }
273 
274 void handleLog(const rosgraph_msgs::Log& log)
275 {
276  if (log.level >= rosgraph_msgs::Log::ERROR) {
277  notify("error");
278  }
279 }
280 
281 void handleBattery(const sensor_msgs::BatteryState& msg)
282 {
283  for (auto const& voltage : msg.cell_voltage) {
285  voltage > 2.0) { // voltage < 2.0 likely indicates incorrect voltage measurement
286  // notify low battery every time
287  notify("low_battery");
288  }
289  }
290 }
291 
292 int main(int argc, char **argv)
293 {
294  ros::init(argc, argv, "led");
295  ros::NodeHandle nh, nh_priv("~");
296 
297  nh_priv.param("blink_rate", blink_rate, 2.0);
298  nh_priv.param("blink_fast_rate", blink_fast_rate, blink_rate * 2);
299  nh_priv.param("fade_period", fade_period, 0.5);
300  nh_priv.param("wipe_period", wipe_period, 0.5);
301  nh_priv.param("flash_delay", flash_delay, 0.1);
302  nh_priv.param("rainbow_period", rainbow_period, 5.0);
303 
304  nh_priv.param("notify/low_battery/threshold", low_battery_threshold, 3.7);
305 
306  ros::service::waitForService("set_leds"); // cannot work without set_leds service
307  set_leds_srv = nh.serviceClient<led_msgs::SetLEDs>("set_leds", true);
308 
309  // wait for leds count info
310  handleState(*ros::topic::waitForMessage<led_msgs::LEDStateArray>("state", nh));
311 
312  auto state_sub = nh.subscribe("state", 1, &handleState);
313 
314  auto set_effect = nh.advertiseService("set_effect", &setEffect);
315 
316  auto mavros_state_sub = nh.subscribe("/mavros/state", 1, &handleMavrosState);
317  auto battery_sub = nh.subscribe("/mavros/battery", 1, &handleBattery);
318  auto rosout_sub = nh.subscribe("/rosout_agg", 1, &handleLog);
319 
320  timer = nh.createTimer(ros::Duration(0), &proceed, false, false);
321 
322  ROS_INFO("ready");
323  notify("startup");
324  ros::spin();
325 }
ServiceClient serviceClient(const std::string &service_name, bool persistent=false, const M_string &header_values=M_string())
led_msgs::LEDStateArray start_state
Definition: led.cpp:34
ros::Time start_time
Definition: led.cpp:29
double low_battery_threshold
Definition: led.cpp:31
led_msgs::LEDStateArray state
Definition: led.cpp:34
bool param(const std::string &param_name, T &param_val, const T &default_val)
bool blink_state
Definition: led.cpp:32
float voltage
ros::NodeHandle nh
ssize_t pos
void rainbow(uint8_t n, uint8_t &r, uint8_t &g, uint8_t &b)
Definition: led.cpp:49
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
double flash_delay
Definition: led.cpp:30
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
int main(int argc, char **argv)
Definition: led.cpp:292
double blink_rate
Definition: led.cpp:30
void stop()
void setPeriod(const Duration &period, bool reset=true)
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
bool call(MReq &req, MRes &res)
void start()
ROSCPP_DECL bool validate(const std::string &name, std::string &error)
void notify(const std::string &event)
Definition: led.cpp:233
int led_count
Definition: led.cpp:27
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
led_msgs::SetLEDs set_leds
Definition: led.cpp:33
mavros_msgs::State mavros_state
Definition: led.cpp:36
int16_t r
Definition: rc.cpp:247
void callSetLeds()
Definition: led.cpp:39
std::string mode
void fill(uint8_t r, uint8_t g, uint8_t b)
Definition: led.cpp:68
bool param(const std::string &param_name, T &param_val, const T &default_val) const
#define ROS_INFO_THROTTLE(period,...)
void handleMavrosState(const mavros_msgs::State &msg)
Definition: led.cpp:249
#define ROS_WARN_THROTTLE(period,...)
double blink_fast_rate
Definition: led.cpp:30
#define ROS_INFO(...)
ROSCPP_DECL bool has(const std::string &key)
ROSCPP_DECL void spin()
bool setEffect(clover::SetLEDEffect::Request &req, clover::SetLEDEffect::Response &res)
Definition: led.cpp:147
void handleLog(const rosgraph_msgs::Log &log)
Definition: led.cpp:274
ros::Timer timer
Definition: led.cpp:28
void proceed(const ros::TimerEvent &event)
Definition: led.cpp:80
double fade_period
Definition: led.cpp:30
static Time now()
double wipe_period
Definition: led.cpp:30
void handleState(const led_msgs::LEDStateArray &msg)
Definition: led.cpp:227
clover::SetLEDEffect::Request current_effect
Definition: led.cpp:26
res
bool sleep() const
void handleBattery(const sensor_msgs::BatteryState &msg)
Definition: led.cpp:281
ros::ServiceClient set_leds_srv
Definition: led.cpp:35
#define ROS_ERROR(...)
ROSCPP_DECL bool waitForService(const std::string &service_name, int32_t timeout)
double rainbow_period
Definition: led.cpp:30
int counter
Definition: led.cpp:37


clover
Author(s): Oleg Kalachev , Artem Smirnov
autogenerated on Mon Feb 28 2022 22:08:29