2024-11-16 07:14:01 -04:00
|
|
|
/*
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "AP_Servo_Telem.h"
|
|
|
|
|
|
|
|
#if AP_SERVO_TELEM_ENABLED
|
|
|
|
|
|
|
|
#include <AP_HAL/AP_HAL.h>
|
|
|
|
#include <AP_Logger/AP_Logger.h>
|
|
|
|
#include <AP_BoardConfig/AP_BoardConfig.h>
|
|
|
|
|
|
|
|
AP_Servo_Telem *AP_Servo_Telem::_singleton;
|
|
|
|
|
|
|
|
AP_Servo_Telem::AP_Servo_Telem()
|
|
|
|
{
|
|
|
|
if (_singleton) {
|
|
|
|
AP_HAL::panic("Too many AP_Servo_Telem instances");
|
|
|
|
}
|
|
|
|
_singleton = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return true if the data is stale
|
|
|
|
bool AP_Servo_Telem::TelemetryData::stale(uint32_t now_ms) const volatile
|
|
|
|
{
|
|
|
|
return (last_update_ms == 0) || ((now_ms - last_update_ms) > 5000);
|
|
|
|
}
|
|
|
|
|
|
|
|
// return true if the requested types of data are available
|
|
|
|
bool AP_Servo_Telem::TelemetryData::present(const uint16_t type_mask) const volatile
|
|
|
|
{
|
|
|
|
return (valid_types & type_mask) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return true if the requested types of data are available and not stale
|
|
|
|
bool AP_Servo_Telem::TelemetryData::valid(const uint16_t type_mask) const volatile
|
|
|
|
{
|
|
|
|
return present(type_mask) && !stale(AP_HAL::millis());
|
|
|
|
}
|
|
|
|
|
|
|
|
// record an update to the telemetry data together with timestamp
|
|
|
|
// callback to update the data in the frontend, should be called by the driver when new data is available
|
|
|
|
void AP_Servo_Telem::update_telem_data(const uint8_t servo_index, const TelemetryData& new_data)
|
|
|
|
{
|
|
|
|
// telemetry data are not protected by a semaphore even though updated from different threads
|
|
|
|
// each element is a primitive type and the timestamp is only updated at the end, thus a caller
|
|
|
|
// can only get slightly more up-to-date information that perhaps they were expecting or might
|
|
|
|
// read data that has just gone stale - both of these are safe and avoid the overhead of locking
|
|
|
|
|
|
|
|
if (servo_index >= ARRAY_SIZE(_telem_data) || (new_data.valid_types == 0)) {
|
|
|
|
return;
|
|
|
|
}
|
2024-11-18 17:19:58 -04:00
|
|
|
active_mask |= 1U << servo_index;
|
2024-11-16 07:14:01 -04:00
|
|
|
|
|
|
|
volatile TelemetryData &telemdata = _telem_data[servo_index];
|
|
|
|
|
|
|
|
if (new_data.present(TelemetryData::Types::COMMANDED_POSITION)) {
|
|
|
|
telemdata.command_position = new_data.command_position;
|
|
|
|
}
|
|
|
|
if (new_data.present(TelemetryData::Types::MEASURED_POSITION)) {
|
|
|
|
telemdata.measured_position = new_data.measured_position;
|
|
|
|
}
|
|
|
|
if (new_data.present(TelemetryData::Types::FORCE)) {
|
|
|
|
telemdata.force = new_data.force;
|
|
|
|
}
|
|
|
|
if (new_data.present(TelemetryData::Types::SPEED)) {
|
|
|
|
telemdata.speed = new_data.speed;
|
|
|
|
}
|
|
|
|
if (new_data.present(TelemetryData::Types::VOLTAGE)) {
|
|
|
|
telemdata.voltage = new_data.voltage;
|
|
|
|
}
|
|
|
|
if (new_data.present(TelemetryData::Types::CURRENT)) {
|
|
|
|
telemdata.current = new_data.current;
|
|
|
|
}
|
|
|
|
if (new_data.present(TelemetryData::Types::DUTY_CYCLE)) {
|
|
|
|
telemdata.duty_cycle = new_data.duty_cycle;
|
|
|
|
}
|
|
|
|
if (new_data.present(TelemetryData::Types::MOTOR_TEMP)) {
|
|
|
|
telemdata.motor_temperature_cdeg = new_data.motor_temperature_cdeg;
|
|
|
|
}
|
|
|
|
if (new_data.present(TelemetryData::Types::PCB_TEMP)) {
|
|
|
|
telemdata.pcb_temperature_cdeg = new_data.pcb_temperature_cdeg;
|
|
|
|
}
|
|
|
|
if (new_data.present(TelemetryData::Types::PCB_TEMP)) {
|
|
|
|
telemdata.status_flags = new_data.status_flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
telemdata.valid_types |= new_data.valid_types;
|
|
|
|
telemdata.last_update_ms = AP_HAL::millis();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AP_Servo_Telem::update()
|
|
|
|
{
|
|
|
|
#if HAL_LOGGING_ENABLED
|
|
|
|
write_log();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAL_LOGGING_ENABLED
|
|
|
|
void AP_Servo_Telem::write_log()
|
|
|
|
{
|
|
|
|
AP_Logger *logger = AP_Logger::get_singleton();
|
|
|
|
|
|
|
|
// Check logging is available and enabled
|
2024-11-18 17:19:58 -04:00
|
|
|
if ((logger == nullptr) || !logger->logging_enabled() || active_mask == 0) {
|
2024-11-16 07:14:01 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint64_t now_us = AP_HAL::micros64();
|
|
|
|
|
|
|
|
for (uint8_t i = 0; i < ARRAY_SIZE(_telem_data); i++) {
|
|
|
|
const volatile TelemetryData &telemdata = _telem_data[i];
|
|
|
|
|
|
|
|
if (telemdata.last_update_ms == _last_telem_log_ms[i]) {
|
|
|
|
// No new data since last log call, skip
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update last log timestamp
|
|
|
|
_last_telem_log_ms[i] = telemdata.last_update_ms;
|
|
|
|
|
|
|
|
// Log, use nan for float values which are not available
|
|
|
|
const struct log_CSRV pkt {
|
|
|
|
LOG_PACKET_HEADER_INIT(LOG_CSRV_MSG),
|
|
|
|
time_us : now_us,
|
|
|
|
id : i,
|
2024-11-18 17:19:58 -04:00
|
|
|
position : telemdata.present(TelemetryData::Types::MEASURED_POSITION) ? telemdata.measured_position : AP::logger().quiet_nanf(),
|
|
|
|
force : telemdata.present(TelemetryData::Types::FORCE) ? telemdata.force : AP::logger().quiet_nanf(),
|
|
|
|
speed : telemdata.present(TelemetryData::Types::SPEED) ? telemdata.speed : AP::logger().quiet_nanf(),
|
2024-11-16 07:14:01 -04:00
|
|
|
power_pct : telemdata.duty_cycle,
|
2024-11-18 17:19:58 -04:00
|
|
|
pos_cmd : telemdata.present(TelemetryData::Types::COMMANDED_POSITION) ? telemdata.command_position : AP::logger().quiet_nanf(),
|
|
|
|
voltage : telemdata.present(TelemetryData::Types::VOLTAGE) ? telemdata.voltage : AP::logger().quiet_nanf(),
|
|
|
|
current : telemdata.present(TelemetryData::Types::CURRENT) ? telemdata.current : AP::logger().quiet_nanf(),
|
|
|
|
mot_temp : telemdata.present(TelemetryData::Types::MOTOR_TEMP) ? telemdata.motor_temperature_cdeg * 0.01 : AP::logger().quiet_nanf(),
|
|
|
|
pcb_temp : telemdata.present(TelemetryData::Types::PCB_TEMP) ? telemdata.pcb_temperature_cdeg * 0.01 : AP::logger().quiet_nanf(),
|
2024-11-16 07:14:01 -04:00
|
|
|
error : telemdata.status_flags,
|
|
|
|
};
|
|
|
|
AP::logger().WriteBlock(&pkt, sizeof(pkt));
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
#endif // HAL_LOGGING_ENABLED
|
|
|
|
|
|
|
|
// Get the AP_Servo_Telem singleton
|
|
|
|
AP_Servo_Telem *AP_Servo_Telem::get_singleton()
|
|
|
|
{
|
|
|
|
return AP_Servo_Telem::_singleton;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // AP_SERVO_TELEM_ENABLED
|