st3215/
ffi.rs

1//! Interface FFI pour l'utilisation depuis C/C++
2//!
3//! Ce module expose les fonctions de la bibliothèque ST3215 via une interface C
4//! compatible, permettant l'utilisation depuis C++ et d'autres langages.
5
6use crate::st3215::ST3215;
7use std::ffi::{CStr, CString};
8use std::os::raw::c_char;
9use std::ptr;
10
11/// Handle opaque pour ST3215
12pub struct ST3215Handle {
13    inner: ST3215,
14}
15
16/// Créer une nouvelle instance ST3215
17/// 
18/// # Arguments
19/// * `device` - Chemin du port série (ex: "/dev/ttyUSB0" ou "COM3")
20///
21/// # Retour
22/// Un pointeur vers ST3215Handle, ou NULL en cas d'erreur
23#[unsafe(no_mangle)]
24pub extern "C" fn st3215_new(device: *const c_char) -> *mut ST3215Handle {
25    if device.is_null() {
26        return ptr::null_mut();
27    }
28
29    let device_str = unsafe {
30        match CStr::from_ptr(device).to_str() {
31            Ok(s) => s,
32            Err(_) => return ptr::null_mut(),
33        }
34    };
35
36    match ST3215::new(device_str) {
37        Ok(st) => Box::into_raw(Box::new(ST3215Handle { inner: st })),
38        Err(_) => ptr::null_mut(),
39    }
40}
41
42/// Libérer une instance ST3215
43///
44/// # Arguments
45/// * `handle` - Handle ST3215 à libérer
46#[unsafe(no_mangle)]
47pub extern "C" fn st3215_free(handle: *mut ST3215Handle) {
48    if !handle.is_null() {
49        unsafe {
50            let _ = Box::from_raw(handle);
51        }
52    }
53}
54
55/// Vérifier la présence d'un servo
56///
57/// # Arguments
58/// * `handle` - Handle ST3215
59/// * `servo_id` - ID du servo (0-253)
60///
61/// # Retour
62/// 1 si le servo répond, 0 sinon
63#[unsafe(no_mangle)]
64pub extern "C" fn st3215_ping_servo(handle: *mut ST3215Handle, servo_id: u8) -> i32 {
65    if handle.is_null() {
66        return 0;
67    }
68
69    let st = unsafe { &(*handle).inner };
70    if st.ping_servo(servo_id) {
71        1
72    } else {
73        0
74    }
75}
76
77/// Lister tous les servos connectés
78///
79/// # Arguments
80/// * `handle` - Handle ST3215
81/// * `out_ids` - Buffer pour stocker les IDs trouvés
82/// * `max_ids` - Taille maximale du buffer
83///
84/// # Retour
85/// Nombre de servos trouvés
86#[unsafe(no_mangle)]
87pub extern "C" fn st3215_list_servos(
88    handle: *mut ST3215Handle,
89    out_ids: *mut u8,
90    max_ids: usize,
91) -> usize {
92    if handle.is_null() || out_ids.is_null() {
93        return 0;
94    }
95
96    let st = unsafe { &(*handle).inner };
97    let servos = st.list_servos();
98    let count = servos.len().min(max_ids);
99
100    unsafe {
101        let slice = std::slice::from_raw_parts_mut(out_ids, count);
102        slice.copy_from_slice(&servos[..count]);
103    }
104
105    count
106}
107
108/// Déplacer un servo vers une position cible
109///
110/// # Arguments
111/// * `handle` - Handle ST3215
112/// * `servo_id` - ID du servo
113/// * `position` - Position cible (0-4095)
114/// * `speed` - Vitesse de déplacement (0-4095)
115/// * `acceleration` - Accélération (0-254)
116///
117/// # Retour
118/// 0 en cas de succès, -1 en cas d'erreur
119#[unsafe(no_mangle)]
120pub extern "C" fn st3215_move_to(
121    handle: *mut ST3215Handle,
122    servo_id: u8,
123    position: u16,
124    speed: u16,
125    acceleration: u8,
126) -> i32 {
127    if handle.is_null() {
128        return -1;
129    }
130
131    let st = unsafe { &(*handle).inner };
132    match st.move_to(servo_id, position, speed, acceleration, false) {
133        Some(_) => 0,
134        None => -1,
135    }
136}
137
138/// Lire la position actuelle d'un servo
139///
140/// # Arguments
141/// * `handle` - Handle ST3215
142/// * `servo_id` - ID du servo
143/// * `out_position` - Pointeur pour stocker la position lue
144///
145/// # Retour
146/// 0 en cas de succès, -1 en cas d'erreur
147#[unsafe(no_mangle)]
148pub extern "C" fn st3215_read_position(
149    handle: *mut ST3215Handle,
150    servo_id: u8,
151    out_position: *mut u16,
152) -> i32 {
153    if handle.is_null() || out_position.is_null() {
154        return -1;
155    }
156
157    let st = unsafe { &(*handle).inner };
158    match st.read_position(servo_id) {
159        Some(pos) => {
160            unsafe {
161                *out_position = pos;
162            }
163            0
164        }
165        None => -1,
166    }
167}
168
169/// Lire la vitesse actuelle d'un servo
170///
171/// # Arguments
172/// * `handle` - Handle ST3215
173/// * `servo_id` - ID du servo
174/// * `out_speed` - Pointeur pour stocker la vitesse lue
175///
176/// # Retour
177/// 0 en cas de succès, -1 en cas d'erreur
178#[unsafe(no_mangle)]
179pub extern "C" fn st3215_read_speed(
180    handle: *mut ST3215Handle,
181    servo_id: u8,
182    out_speed: *mut u16,
183) -> i32 {
184    if handle.is_null() || out_speed.is_null() {
185        return -1;
186    }
187
188    let st = unsafe { &(*handle).inner };
189    match st.read_speed(servo_id) {
190        Some(speed) => {
191            unsafe {
192                *out_speed = speed.abs() as u16;
193            }
194            0
195        }
196        None => -1,
197    }
198}
199
200/// Lire la charge d'un servo (en pourcentage)
201///
202/// # Arguments
203/// * `handle` - Handle ST3215
204/// * `servo_id` - ID du servo
205/// * `out_load` - Pointeur pour stocker la charge lue
206///
207/// # Retour
208/// 0 en cas de succès, -1 en cas d'erreur
209#[unsafe(no_mangle)]
210pub extern "C" fn st3215_read_load(
211    handle: *mut ST3215Handle,
212    servo_id: u8,
213    out_load: *mut f32,
214) -> i32 {
215    if handle.is_null() || out_load.is_null() {
216        return -1;
217    }
218
219    let st = unsafe { &(*handle).inner };
220    match st.read_load(servo_id) {
221        Some(load) => {
222            unsafe {
223                *out_load = load;
224            }
225            0
226        }
227        None => -1,
228    }
229}
230
231/// Lire la tension d'un servo (en volts)
232///
233/// # Arguments
234/// * `handle` - Handle ST3215
235/// * `servo_id` - ID du servo
236/// * `out_voltage` - Pointeur pour stocker la tension lue
237///
238/// # Retour
239/// 0 en cas de succès, -1 en cas d'erreur
240#[unsafe(no_mangle)]
241pub extern "C" fn st3215_read_voltage(
242    handle: *mut ST3215Handle,
243    servo_id: u8,
244    out_voltage: *mut f32,
245) -> i32 {
246    if handle.is_null() || out_voltage.is_null() {
247        return -1;
248    }
249
250    let st = unsafe { &(*handle).inner };
251    match st.read_voltage(servo_id) {
252        Some(voltage) => {
253            unsafe {
254                *out_voltage = voltage;
255            }
256            0
257        }
258        None => -1,
259    }
260}
261
262/// Lire le courant d'un servo (en mA)
263///
264/// # Arguments
265/// * `handle` - Handle ST3215
266/// * `servo_id` - ID du servo
267/// * `out_current` - Pointeur pour stocker le courant lu
268///
269/// # Retour
270/// 0 en cas de succès, -1 en cas d'erreur
271#[unsafe(no_mangle)]
272pub extern "C" fn st3215_read_current(
273    handle: *mut ST3215Handle,
274    servo_id: u8,
275    out_current: *mut f32,
276) -> i32 {
277    if handle.is_null() || out_current.is_null() {
278        return -1;
279    }
280
281    let st = unsafe { &(*handle).inner };
282    match st.read_current(servo_id) {
283        Some(current) => {
284            unsafe {
285                *out_current = current;
286            }
287            0
288        }
289        None => -1,
290    }
291}
292
293/// Lire la température d'un servo (en °C)
294///
295/// # Arguments
296/// * `handle` - Handle ST3215
297/// * `servo_id` - ID du servo
298/// * `out_temperature` - Pointeur pour stocker la température lue
299///
300/// # Retour
301/// 0 en cas de succès, -1 en cas d'erreur
302#[unsafe(no_mangle)]
303pub extern "C" fn st3215_read_temperature(
304    handle: *mut ST3215Handle,
305    servo_id: u8,
306    out_temperature: *mut u8,
307) -> i32 {
308    if handle.is_null() || out_temperature.is_null() {
309        return -1;
310    }
311
312    let st = unsafe { &(*handle).inner };
313    match st.read_temperature(servo_id) {
314        Some(temp) => {
315            unsafe {
316                *out_temperature = temp;
317            }
318            0
319        }
320        None => -1,
321    }
322}
323
324/// Vérifier si un servo est en mouvement
325///
326/// # Arguments
327/// * `handle` - Handle ST3215
328/// * `servo_id` - ID du servo
329///
330/// # Retour
331/// 1 si en mouvement, 0 si arrêté, -1 en cas d'erreur
332#[unsafe(no_mangle)]
333pub extern "C" fn st3215_is_moving(handle: *mut ST3215Handle, servo_id: u8) -> i32 {
334    if handle.is_null() {
335        return -1;
336    }
337
338    let st = unsafe { &(*handle).inner };
339    match st.is_moving(servo_id) {
340        Some(true) => 1,
341        Some(false) => 0,
342        None => -1,
343    }
344}
345
346/// Activer le couple d'un servo
347///
348/// # Arguments
349/// * `handle` - Handle ST3215
350/// * `servo_id` - ID du servo
351/// * `enable` - 1 pour activer, 0 pour désactiver
352///
353/// # Retour
354/// 0 en cas de succès, -1 en cas d'erreur
355#[unsafe(no_mangle)]
356pub extern "C" fn st3215_enable_torque(
357    handle: *mut ST3215Handle,
358    servo_id: u8,
359    enable: i32,
360) -> i32 {
361    if handle.is_null() {
362        return -1;
363    }
364
365    let st = unsafe { &(*handle).inner };
366    let result = if enable != 0 {
367        st.enable_torque(servo_id)
368    } else {
369        st.disable_torque(servo_id)
370    };
371    match result {
372        Ok(_) => 0,
373        Err(_) => -1,
374    }
375}
376
377/// Obtenir la version de la bibliothèque
378///
379/// # Retour
380/// Chaîne de caractères contenant la version (doit être libérée avec st3215_free_string)
381#[unsafe(no_mangle)]
382pub extern "C" fn st3215_version() -> *mut c_char {
383    let version = env!("CARGO_PKG_VERSION");
384    match CString::new(version) {
385        Ok(s) => s.into_raw(),
386        Err(_) => ptr::null_mut(),
387    }
388}
389
390/// Libérer une chaîne de caractères allouée par la bibliothèque
391///
392/// # Arguments
393/// * `s` - Pointeur vers la chaîne à libérer
394#[unsafe(no_mangle)]
395pub extern "C" fn st3215_free_string(s: *mut c_char) {
396    if !s.is_null() {
397        unsafe {
398            let _ = CString::from_raw(s);
399        }
400    }
401}