ev3rt/
lib.rs

1#![no_std]
2
3extern crate alloc;
4
5struct Ev3Allocator {}
6#[global_allocator]
7static GLOBAL_ALLOCATOR: Ev3Allocator = Ev3Allocator {};
8
9unsafe impl GlobalAlloc for Ev3Allocator {
10    unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
11        ev3_malloc(layout.size())
12            .cast::<u8>()
13            .as_mut()
14            .expect("ev3_malloc failed")
15    }
16
17    unsafe fn dealloc(&self, ptr: *mut u8, _layout: core::alloc::Layout) {
18        ev3_free(ptr.cast::<core::ffi::c_void>())
19    }
20}
21
22pub fn reset(is_panic: bool) {
23    motor_stop(MotorPort::A, false);
24    motor_stop(MotorPort::B, false);
25    motor_stop(MotorPort::C, false);
26    motor_stop(MotorPort::D, false);
27    sensor_config(SensorPort::S1, SensorType::NONE);
28    sensor_config(SensorPort::S2, SensorType::NONE);
29    sensor_config(SensorPort::S3, SensorType::NONE);
30    sensor_config(SensorPort::S4, SensorType::NONE);
31    led_set_color(LedColor::OFF);
32    if is_panic {
33        lcd_apply(|fb| {
34            for (index, row) in fb.chunks_exact_mut(LCD_FRAMEBUFFER_ROW_BYTES).enumerate() {
35                if index % 2 == 0 {
36                    row.fill(0b10101010);
37                } else {
38                    row.fill(0b01010101);
39                }
40            }
41        });
42        for _ in 0..4 {
43            flash_led(LedColor::RED);
44        }
45    } else {
46        lcd_clear();
47    }
48}
49
50#[no_mangle]
51pub extern "C" fn abort() -> ! {
52    unsafe { ev3_exit_task() }
53    #[warn(clippy::empty_loop)]
54    loop {}
55}
56
57const PANIC_DATA_SIZE: usize = 512;
58const PANIC_FILE: &str = "PANIC.txt";
59struct PanicData {
60    data: [u8; PANIC_DATA_SIZE],
61    offset: usize,
62}
63
64impl PanicData {
65    pub fn new() -> Self {
66        PanicData {
67            data: [0; PANIC_DATA_SIZE],
68            offset: 0,
69        }
70    }
71
72    pub fn close(&mut self) {
73        if self.offset >= PANIC_DATA_SIZE {
74            self.offset = PANIC_DATA_SIZE - 1;
75        }
76        self.data[self.offset] = '\n' as u8;
77        self.offset = PANIC_DATA_SIZE;
78    }
79
80    pub fn save(&self) {
81        let mut file = MemFile::new();
82        file.load(PANIC_FILE);
83        if let Some(buffer) = file.buffer() {
84            let data = buffer.data_mut();
85            if data.len() == PANIC_DATA_SIZE {
86                data.copy_from_slice(&self.data);
87                buffer.write(PANIC_FILE);
88            }
89        }
90        file.free();
91    }
92}
93
94pub fn reset_ev3_panic_file() {
95    let mut data = PanicData::new();
96    data.write_str("panic data ready").ok();
97    data.close();
98    data.save();
99}
100
101impl Write for PanicData {
102    fn write_str(&mut self, s: &str) -> core::fmt::Result {
103        for c in s.bytes() {
104            if self.offset < PANIC_DATA_SIZE {
105                self.data[self.offset] = c;
106                self.offset += 1;
107            }
108        }
109        core::fmt::Result::Ok(())
110    }
111}
112
113fn flash_led(color: LedColor) {
114    led_set_color(color);
115    msleep(500);
116    led_set_color(LedColor::OFF);
117    msleep(500);
118}
119
120use core::{alloc::GlobalAlloc, fmt::Write};
121#[cfg(not(test))]
122#[panic_handler]
123fn panic(info: &core::panic::PanicInfo) -> ! {
124    reset(true);
125    let mut data = PanicData::new();
126    if let Some(location) = info.location() {
127        writeln!(
128            &mut data,
129            "panic at {}:{}",
130            //location.file(),
131            "FILE",
132            location.line()
133        )
134        .ok();
135    } else {
136        writeln!(&mut data, "panic at unknown location").ok();
137    }
138    writeln!(&mut data, "message: {}", info.message()).ok();
139    data.close();
140    data.save();
141    abort();
142}
143
144#[repr(i32)]
145#[derive(Clone, Copy, PartialEq)]
146pub enum ER {
147    OK = 0,
148    SYS = -5,
149    NOSPT = -9,
150    RSFN = -10,
151    RSATR = -11,
152    PAR = -17,
153    ID = -18,
154    CTX = -25,
155    MACV = -26,
156    OACV = -27,
157    ILUSE = -28,
158    NOMEM = -33,
159    NOID = -34,
160    NORES = -35,
161    OBJ = -41,
162    NOEXS = -42,
163    QOVR = -43,
164    RLWAI = -49,
165    TMOUT = -50,
166    DLT = -51,
167    CLS = -52,
168    WBLK = -57,
169    BOVR = -58,
170}
171pub type ErUint = i32;
172
173#[repr(i32)]
174#[derive(Clone, Copy, PartialEq)]
175pub enum LedColor {
176    OFF = 0,
177    RED = 1,
178    GREEN = 2,
179    ORANGE = 3,
180}
181
182#[repr(i32)]
183#[derive(Clone, Copy, PartialEq)]
184pub enum Button {
185    LEFT = 0,
186    RIGHT = 1,
187    UP = 2,
188    DOWN = 3,
189    ENTER = 4,
190    BACK = 5,
191}
192
193#[repr(i32)]
194#[derive(Clone, Copy, PartialEq)]
195pub enum SerialPort {
196    DEFAULT = 0,
197    UART = 1,
198    BLUETOOTH = 2,
199}
200
201#[repr(i32)]
202#[derive(Clone, Copy, PartialEq)]
203pub enum LcdFont {
204    SMALL = 0,
205    MEDIUM = 1,
206}
207
208#[repr(i32)]
209#[derive(Clone, Copy, PartialEq)]
210pub enum LcdColor {
211    WHITE = 0,
212    BLACK = 1,
213}
214
215pub const LCD_WIDTH: i32 = 178;
216pub const LCD_HEIGHT: i32 = 128;
217pub const LCD_FRAMEBUFFER_ROW_BYTES: usize = 60;
218pub const LCD_FRAMEBUFFER_ROWS: usize = 128;
219pub const LCD_FRAMEBUFFER_SIZE: usize = LCD_FRAMEBUFFER_ROW_BYTES * LCD_FRAMEBUFFER_ROWS;
220
221#[repr(i32)]
222#[derive(Clone, Copy, PartialEq)]
223pub enum MotorPort {
224    A = 0,
225    B = 1,
226    C = 2,
227    D = 3,
228}
229
230#[repr(i32)]
231#[derive(Clone, Copy, PartialEq)]
232pub enum MotorType {
233    NONE = 0,
234    MEDIUM = 1,
235    LARGE = 2,
236    UNDEGULATED = 3,
237}
238impl From<i32> for MotorType {
239    fn from(t: i32) -> MotorType {
240        match t {
241            1 => MotorType::MEDIUM,
242            2 => MotorType::LARGE,
243            3 => MotorType::UNDEGULATED,
244            _ => MotorType::NONE,
245        }
246    }
247}
248
249#[repr(i32)]
250#[derive(Clone, Copy, PartialEq)]
251pub enum SensorPort {
252    S1 = 0,
253    S2 = 1,
254    S3 = 2,
255    S4 = 3,
256}
257
258#[repr(i32)]
259#[derive(Clone, Copy, PartialEq)]
260pub enum SensorType {
261    NONE = 0,
262    ULTRASONIC = 1,
263    GYRO = 2,
264    TOUCH = 3,
265    COLOR = 4,
266    INFRARED = 5,
267    HtNxtACCEL = 6,
268    HtNxtCOLOR = 7,
269    NxtULTRASONIC = 8,
270    NxtTEMP = 9,
271}
272impl From<i32> for SensorType {
273    fn from(t: i32) -> SensorType {
274        match t {
275            1 => SensorType::ULTRASONIC,
276            2 => SensorType::GYRO,
277            3 => SensorType::TOUCH,
278            4 => SensorType::COLOR,
279            5 => SensorType::INFRARED,
280            6 => SensorType::HtNxtACCEL,
281            7 => SensorType::HtNxtCOLOR,
282            8 => SensorType::NxtULTRASONIC,
283            9 => SensorType::NxtTEMP,
284            _ => SensorType::NONE,
285        }
286    }
287}
288
289#[repr(i32)]
290pub enum SensorColorCode {
291    NONE = 0,
292    BLACK = 1,
293    BLUE = 2,
294    GREEN = 3,
295    YELLOW = 4,
296    RED = 5,
297    WHITE = 6,
298    BROWN = 7,
299}
300
301#[repr(C)]
302#[derive(Debug, Copy, Clone)]
303pub struct RgbRaw {
304    ///!< \~English Red value   \~Japanese 赤
305    pub r: u16,
306    ///!< \~English Green value \~Japanese 緑
307    pub g: u16,
308    ///!< \~English Blue value  \~Japanese 青
309    pub b: u16,
310}
311
312#[repr(C)]
313#[derive(Debug, Copy, Clone)]
314pub struct IrSeek {
315    ///!< \~English Heading  for channels 1-4 (-25 to 25)           \~Japanese 全て(4つ)のチャンネルの方位(-25~25)
316    pub heading: [i8; 4usize],
317    ///!< \~English Distance for channels 1-4 (-128 and 0 to 100)   \~Japanese 全て(4つ)のチャンネルの距離(0〜100または-128)
318    pub distance: [i8; 4usize],
319}
320
321#[repr(C)]
322#[derive(Debug, Copy, Clone)]
323pub struct IrRemote {
324    ///!< \~English IR Remote controller data for channels 1-4   \~Japanese 全て(4つ)のチャンネルのボタン入力のパタン
325    pub channel: [u8; 4usize],
326}
327
328#[repr(C)]
329#[derive(Debug, Copy, Clone)]
330pub struct MemFile {
331    buffer: *const u8,
332    filesz: u32,
333    buffersz: u32,
334}
335
336impl MemFile {
337    pub fn new() -> Self {
338        MemFile {
339            buffer: core::ptr::null(),
340            filesz: 0,
341            buffersz: 0,
342        }
343    }
344
345    pub fn load(&mut self, path: &str) -> ER {
346        memfile_load(path, self)
347    }
348
349    pub fn data(&self) -> Option<&[u8]> {
350        if self.buffer.is_null() {
351            None
352        } else {
353            Some(unsafe { core::slice::from_raw_parts(self.buffer, self.filesz as usize) })
354        }
355    }
356
357    pub fn buffer(self) -> Option<MemFileBuffer> {
358        let mut memfile = self;
359        if memfile.buffer.is_null() {
360            memfile_free(&mut memfile);
361            None
362        } else {
363            Some(MemFileBuffer {
364                buffer: memfile.buffer as *mut u8,
365                filesz: memfile.filesz,
366                buffersz: memfile.buffersz,
367            })
368        }
369    }
370
371    pub fn free(self) {
372        let mut memfile = self;
373        memfile_free(&mut memfile);
374    }
375}
376
377#[repr(C)]
378#[derive(Debug, Copy, Clone)]
379pub struct MemFileBuffer {
380    buffer: *mut u8,
381    filesz: u32,
382    buffersz: u32,
383}
384
385impl MemFileBuffer {
386    pub fn data(&self) -> &[u8] {
387        unsafe { core::slice::from_raw_parts(self.buffer, self.filesz as usize) }
388    }
389
390    pub fn data_mut(&self) -> &mut [u8] {
391        unsafe { core::slice::from_raw_parts_mut(self.buffer, self.filesz as usize) }
392    }
393
394    pub fn write(&self, path: &str) -> ER {
395        file_write(path, self.data())
396    }
397
398    pub fn memfile(self) -> MemFile {
399        MemFile {
400            buffer: self.buffer,
401            filesz: self.filesz,
402            buffersz: self.buffersz,
403        }
404    }
405
406    pub fn free(self) {
407        let mut memfile = self.memfile();
408        memfile_free(&mut memfile);
409    }
410}
411
412// pub fn file_write(path: &str, data: &[u8]) -> ER {
413//     let path = str2path(path);
414//     unsafe { ev3_file_write(path.as_ptr(), data.as_ptr(), data.len() as u32) }
415// }
416
417pub type TMO = i32;
418pub type RELTIM = u32;
419pub type HRTCNT = u32;
420pub type SYSTIM = i64;
421pub type SYSUTM = u64;
422pub type BoolT = i8;
423
424extern "C" {
425    fn ev3_malloc(size: usize) -> *mut core::ffi::c_void;
426    fn ev3_free(ptr: *mut core::ffi::c_void) -> ();
427
428    fn ev3_exit_task() -> ();
429    fn ev3_get_utm(p_sysutm: &mut SYSUTM) -> ER;
430    fn ev3_sleep(ticks: i32) -> ER;
431
432    fn ev3_bluetooth_agent_set_period_ms(ms: u32);
433    fn ev3_bluetooth_agent_set_master(is_master: BoolT);
434    fn ev3_check_bluetooth_is_connected() -> BoolT;
435    fn ev3_schedule_bluetooth_agent_task() -> BoolT;
436    fn ev3_bt_write_value(value: u32) -> BoolT;
437    fn ev3_connect_to_bluetooth_device(addr: *const u8, pin: *const u8) -> ER;
438    fn ev3_bluetooth_agent_values_read_ptr() -> *mut u32;
439    fn ev3_bluetooth_agent_value_read_ptr() -> *mut u32;
440
441    fn ev3_battery_current_mA() -> i32;
442    fn ev3_battery_voltage_mV() -> i32;
443    fn ev3_button_is_pressed(button: Button) -> BoolT;
444
445    fn ev3_lcd_set_font(font: LcdFont) -> ER;
446    fn ev3_font_get_size(font: LcdFont, width: *mut i32, height: *mut i32) -> ER;
447    fn ev3_lcd_draw_string(str: *const u8, x: i32, y: i32) -> ER;
448    fn ev3_lcd_draw_line(x0: i32, y0: i32, x1: i32, y1: i32) -> ER;
449    fn ev3_lcd_pixels() -> *mut u8;
450    fn ev3_lcd_fill_rect(x: i32, y: i32, w: i32, h: i32, color: LcdColor) -> ER;
451    fn ev3_motor_config(port: MotorPort, mt: MotorType) -> ER;
452    fn ev3_motor_get_type(port: MotorPort) -> ErUint;
453    fn ev3_motor_get_counts(port: MotorPort) -> i32;
454    fn ev3_motor_get_ticks(port: MotorPort) -> u32;
455    fn ev3_motor_reset_counts(port: MotorPort) -> ER;
456    fn ev3_motor_set_power(port: MotorPort, power: i32) -> ER;
457    fn ev3_motor_get_power(port: MotorPort) -> i32;
458    fn ev3_motor_stop(port: MotorPort, brake: BoolT) -> ER;
459
460    fn ev3_sensor_config(port: SensorPort, st: SensorType) -> ER;
461    fn ev3_sensor_get_type(port: SensorPort) -> ErUint;
462    fn ev3_color_sensor_get_color(port: SensorPort) -> SensorColorCode;
463    fn ev3_color_sensor_get_reflect(port: SensorPort) -> u8;
464    fn ev3_color_sensor_get_ambient(port: SensorPort) -> u8;
465    fn ev3_color_sensor_get_rgb_raw(port: SensorPort, val: *mut RgbRaw);
466    fn ev3_gyro_sensor_get_angle(port: SensorPort) -> i16;
467    fn ev3_gyro_sensor_get_rate(port: SensorPort) -> i16;
468    fn ev3_gyro_sensor_reset(port: SensorPort) -> ER;
469    fn ev3_ultrasonic_sensor_get_distance(port: SensorPort) -> i16;
470    // fn ev3_ultrasonic_sensor_listen(port: SensorPort) -> BoolT;
471
472    fn ev3_infrared_sensor_get_distance(port: SensorPort) -> u8;
473    // fn ev3_infrared_sensor_seek(port: SensorPort) -> IrSeek;
474    // fn ev3_infrared_sensor_get_remote(port: SensorPort) -> IrRemote;
475    fn ev3_touch_sensor_is_pressed(port: SensorPort) -> BoolT;
476    fn ev3_touch_sensor_analog_read_pin1(port: SensorPort) -> i16;
477    // fn ht_nxt_accel_sensor_measure(port: SensorPort, axes: *mut i16) -> BoolT;
478    // fn ht_nxt_color_sensor_measure_color(port: SensorPort, color: *mut u8) -> BoolT;
479    // fn ht_nxt_color_sensor_measure_rgb(port: SensorPort, val: *mut RgbRaw) -> BoolT;
480    // fn nxt_temp_sensor_measure(port: SensorPort, temp: *mut f32) -> BoolT;
481    fn nxt_ultrasonic_sensor_get_last_reading(port: SensorPort) -> i16;
482    fn nxt_ultrasonic_sensor_did_reset(port: SensorPort) -> BoolT;
483    fn nxt_ultrasonic_sensor_request_read(port: SensorPort) -> BoolT;
484    fn nxt_ultrasonic_sensor_request_reset(port: SensorPort) -> BoolT;
485    fn nxt_ultrasonic_sensor_get_distance(port: SensorPort, distance: *mut i16) -> BoolT;
486
487    // fn ev3_speaker_set_volume(volume: u8) -> ER;
488    // fn ev3_speaker_play_tone(frequency: u16, duration: i32) -> ER;
489    // fn ev3_speaker_stop() -> ER;
490
491    fn ev3_memfile_load(path: *const u8, memfile: *mut MemFile) -> ER;
492    fn ev3_memfile_free(memfile: *mut MemFile) -> ER;
493    fn ev3_file_write(path: *const u8, data: *const u8, size: u32) -> ER;
494
495    fn ev3_led_set_color(color: LedColor) -> ER;
496}
497
498pub fn get_utm(p_sysutm: &mut SYSUTM) -> ER {
499    unsafe { ev3_get_utm(p_sysutm) }
500}
501
502pub fn get_utime() -> SYSUTM {
503    let mut res: SYSUTM = 0;
504    match get_utm(&mut res) {
505        ER::OK => res,
506        _ => {
507            panic!("get_utime failed");
508        }
509    }
510}
511
512pub fn msleep(ms: i32) -> ER {
513    unsafe { ev3_sleep(ms) }
514}
515
516#[derive(Clone, Copy, PartialEq, Eq)]
517pub struct BtValue {
518    value: u32,
519}
520
521impl BtValue {
522    pub fn new(value: u32) -> Self {
523        BtValue { value }
524    }
525}
526
527impl From<BtValue> for u32 {
528    fn from(v: BtValue) -> u32 {
529        v.value
530    }
531}
532impl From<u32> for BtValue {
533    fn from(v: u32) -> BtValue {
534        BtValue { value: v }
535    }
536}
537
538impl From<BtValue> for i32 {
539    fn from(v: BtValue) -> i32 {
540        v.value as i32
541    }
542}
543impl From<i32> for BtValue {
544    fn from(v: i32) -> BtValue {
545        BtValue { value: v as u32 }
546    }
547}
548
549impl From<(u16, u16)> for BtValue {
550    fn from(v: (u16, u16)) -> BtValue {
551        BtValue {
552            value: (v.0 as u32) << 16 | v.1 as u32,
553        }
554    }
555}
556impl From<BtValue> for (u16, u16) {
557    fn from(v: BtValue) -> (u16, u16) {
558        ((v.value >> 16) as u16, v.value as u16)
559    }
560}
561
562impl From<(u8, u8, u8, u8)> for BtValue {
563    fn from(v: (u8, u8, u8, u8)) -> BtValue {
564        BtValue {
565            value: (v.0 as u32) << 24 | (v.1 as u32) << 16 | (v.2 as u32) << 8 | v.3 as u32,
566        }
567    }
568}
569impl From<BtValue> for (u8, u8, u8, u8) {
570    fn from(v: BtValue) -> (u8, u8, u8, u8) {
571        (
572            (v.value >> 24) as u8,
573            (v.value >> 16) as u8,
574            (v.value >> 8) as u8,
575            v.value as u8,
576        )
577    }
578}
579
580impl From<(i8, i8, i8, i8)> for BtValue {
581    fn from(v: (i8, i8, i8, i8)) -> BtValue {
582        BtValue {
583            value: ((v.0 as i32 & 0xff) << 24
584                | (v.1 as i32 & 0xff) << 16
585                | (v.2 as i32 & 0xff) << 8
586                | (v.3 as i32 & 0xff)) as u32,
587        }
588    }
589}
590impl From<BtValue> for (i8, i8, i8, i8) {
591    fn from(v: BtValue) -> (i8, i8, i8, i8) {
592        (
593            (v.value >> 24) as i8,
594            (v.value >> 16) as i8,
595            (v.value >> 8) as i8,
596            v.value as i8,
597        )
598    }
599}
600
601#[derive(Clone, Copy)]
602pub struct BT {
603    value: *mut u32,
604    counter: *mut u32,
605}
606
607impl BT {
608    pub fn new_master(slave: &[u8; 6], pin: &[u8; 4], read_period_ms: u32) -> Option<Self> {
609        bluetooth_agent_set_master(true);
610
611        let connected = unsafe {
612            let er = ev3_connect_to_bluetooth_device(slave.as_ptr(), pin.as_ptr());
613            er == ER::OK
614        };
615        if !connected {
616            return None;
617        }
618
619        if !schedule_bluetooth_agent_task() {
620            return None;
621        }
622
623        if !bluetooth_is_connected() {
624            return None;
625        }
626
627        bluetooth_agent_set_period_ms(read_period_ms);
628
629        Some(BT {
630            value: unsafe { ev3_bluetooth_agent_value_read_ptr() },
631            counter: unsafe { ev3_bluetooth_agent_values_read_ptr() },
632        })
633    }
634
635    pub fn new_slave(read_period_ms: u32) -> Option<Self> {
636        bluetooth_agent_set_master(false);
637
638        if !schedule_bluetooth_agent_task() {
639            return None;
640        }
641
642        if !bluetooth_is_connected() {
643            return None;
644        }
645
646        bluetooth_agent_set_period_ms(read_period_ms);
647
648        Some(BT {
649            value: unsafe { ev3_bluetooth_agent_value_read_ptr() },
650            counter: unsafe { ev3_bluetooth_agent_values_read_ptr() },
651        })
652    }
653
654    pub fn write(&self, value: BtValue) -> bool {
655        unsafe { ev3_bt_write_value(value.into()) != 0 }
656    }
657
658    pub fn read(&self) -> BtValue {
659        unsafe { *self.value }.into()
660    }
661
662    pub fn read_count(&self) -> usize {
663        (unsafe { *self.counter }) as usize
664    }
665
666    pub fn is_connected(&self) -> bool {
667        bluetooth_is_connected()
668    }
669
670    pub fn set_read_period(&self, ms: u32) {
671        bluetooth_agent_set_period_ms(ms);
672    }
673}
674
675pub fn bluetooth_agent_set_period_ms(ms: u32) {
676    unsafe {
677        ev3_bluetooth_agent_set_period_ms(ms);
678    }
679}
680
681pub fn bluetooth_agent_set_master(is_master: bool) {
682    unsafe {
683        ev3_bluetooth_agent_set_master(if is_master { 1 } else { 0 });
684    }
685}
686
687pub fn bluetooth_is_connected() -> bool {
688    unsafe { ev3_check_bluetooth_is_connected() != 0 }
689}
690
691pub fn schedule_bluetooth_agent_task() -> bool {
692    unsafe { ev3_schedule_bluetooth_agent_task() != 0 }
693}
694
695pub fn battery_current_ma() -> i32 {
696    unsafe { ev3_battery_current_mA() }
697}
698pub fn battery_voltage_mv() -> i32 {
699    unsafe { ev3_battery_voltage_mV() }
700}
701
702pub fn button_is_pressed(button: Button) -> bool {
703    unsafe { ev3_button_is_pressed(button) != 0 }
704}
705
706pub fn any_button_is_pressed() -> bool {
707    button_is_pressed(Button::BACK)
708        || button_is_pressed(Button::ENTER)
709        || button_is_pressed(Button::UP)
710        || button_is_pressed(Button::DOWN)
711        || button_is_pressed(Button::LEFT)
712        || button_is_pressed(Button::RIGHT)
713}
714
715pub fn wait_for_buttons_released() {
716    while any_button_is_pressed() {}
717}
718
719pub fn lcd_set_font(font: LcdFont) -> ER {
720    unsafe { ev3_lcd_set_font(font) }
721}
722
723pub fn font_get_size(font: LcdFont) -> (i32, i32) {
724    let mut w: i32 = 0;
725    let mut h: i32 = 0;
726    unsafe {
727        let er = ev3_font_get_size(font, &mut w, &mut h);
728        if let ER::OK = er {
729            (w, h)
730        } else {
731            (0, 0)
732        }
733    }
734}
735
736/// Draw a string into lcd from a starting position
737///
738/// The point (0, 0) is in upper-left position
739/// X axis is horizontal, from left to right
740/// Y axis is vertical, from top to bottom
741pub fn lcd_draw_string(s: &str, x: i32, y: i32) -> ER {
742    const MAX: usize = 32;
743    const BYTES: usize = MAX + 1;
744    let mut chars: [u8; BYTES] = [0; BYTES];
745    for (i, c) in s.bytes().enumerate() {
746        if i >= MAX {
747            break;
748        }
749        chars[i] = c;
750        chars[i + 1] = 0;
751    }
752    unsafe { ev3_lcd_draw_string(&(chars[0]), x, y) }
753}
754
755pub fn lcd_draw_line(x0: i32, y0: i32, x1: i32, y1: i32) -> ER {
756    unsafe { ev3_lcd_draw_line(x0, y0, x1, y1) }
757}
758
759pub fn lcd_fill_rect(x: i32, y: i32, w: i32, h: i32, color: LcdColor) -> ER {
760    unsafe { ev3_lcd_fill_rect(x, y, w, h, color) }
761}
762
763pub fn lcd_apply(f: impl Fn(&mut [u8; LCD_FRAMEBUFFER_SIZE])) {
764    unsafe {
765        let pixels = ev3_lcd_pixels().cast::<[u8; LCD_FRAMEBUFFER_SIZE]>();
766        let framebuffer = &mut *pixels;
767        f(framebuffer);
768    }
769}
770
771pub fn lcd_clear() {
772    lcd_apply(|fb| fb.fill(0x00));
773}
774
775pub fn motor_config(port: MotorPort, mt: MotorType) -> ER {
776    unsafe { ev3_motor_config(port, mt) }
777}
778
779pub fn motor_get_type(port: MotorPort) -> MotorType {
780    unsafe {
781        let t = ev3_motor_get_type(port);
782        MotorType::from(t)
783    }
784}
785
786pub fn motor_get_counts(port: MotorPort) -> i32 {
787    unsafe { ev3_motor_get_counts(port) }
788}
789
790pub fn motor_get_ticks(port: MotorPort) -> u32 {
791    unsafe { ev3_motor_get_ticks(port) }
792}
793
794pub fn motor_reset_counts(port: MotorPort) -> ER {
795    unsafe { ev3_motor_reset_counts(port) }
796}
797
798pub fn motor_set_power(port: MotorPort, power: i32) -> ER {
799    unsafe { ev3_motor_set_power(port, power) }
800}
801
802pub fn motor_get_power(port: MotorPort) -> i32 {
803    unsafe { ev3_motor_get_power(port) }
804}
805
806pub fn motor_stop(port: MotorPort, brake: bool) -> ER {
807    unsafe { ev3_motor_stop(port, if brake { 1 } else { 0 }) }
808}
809
810pub fn sensor_config(port: SensorPort, st: SensorType) -> ER {
811    unsafe { ev3_sensor_config(port, st) }
812}
813
814pub fn sensor_get_type(port: SensorPort) -> SensorType {
815    unsafe {
816        let t = ev3_sensor_get_type(port);
817        SensorType::from(t)
818    }
819}
820
821pub fn touch_sensor_is_pressed(port: SensorPort) -> bool {
822    unsafe { ev3_touch_sensor_is_pressed(port) != 0 }
823}
824
825pub fn analog_sensor_read(port: SensorPort) -> i16 {
826    unsafe { ev3_touch_sensor_analog_read_pin1(port) }
827}
828
829pub fn color_sensor_get_color(port: SensorPort) -> SensorColorCode {
830    unsafe { ev3_color_sensor_get_color(port) }
831}
832
833pub fn color_sensor_get_reflect(port: SensorPort) -> u8 {
834    unsafe { ev3_color_sensor_get_reflect(port) }
835}
836
837pub fn color_sensor_get_ambient(port: SensorPort) -> u8 {
838    unsafe { ev3_color_sensor_get_ambient(port) }
839}
840
841pub fn color_sensor_get_rgb(port: SensorPort) -> RgbRaw {
842    let mut r = RgbRaw { r: 0, g: 0, b: 0 };
843    unsafe {
844        ev3_color_sensor_get_rgb_raw(port, &mut r);
845    }
846    r
847}
848
849pub fn gyro_sensor_get_angle(port: SensorPort) -> i16 {
850    unsafe { ev3_gyro_sensor_get_angle(port) }
851}
852
853pub fn gyro_sensor_get_rate(port: SensorPort) -> i16 {
854    unsafe { ev3_gyro_sensor_get_rate(port) }
855}
856
857pub fn gyro_sensor_reset(port: SensorPort) -> ER {
858    unsafe { ev3_gyro_sensor_reset(port) }
859}
860
861pub fn ultrasonic_sensor_get_distance(port: SensorPort) -> i16 {
862    unsafe { ev3_ultrasonic_sensor_get_distance(port) }
863}
864
865// fn ev3_ultrasonic_sensor_listen(port: SensorPort) -> BoolT;
866
867pub fn infrared_sensor_get_distance(port: SensorPort) -> u8 {
868    unsafe { ev3_infrared_sensor_get_distance(port) }
869}
870
871// fn ev3_infrared_sensor_seek(port: SensorPort) -> IrSeek;
872// fn ev3_infrared_sensor_get_remote(port: SensorPort) -> IrRemote;
873// fn ev3_touch_sensor_is_pressed(port: SensorPort) -> BoolT;
874// fn ht_nxt_accel_sensor_measure(port: SensorPort, axes: *mut i16) -> BoolT;
875// fn ht_nxt_color_sensor_measure_color(port: SensorPort, color: *mut u8) -> BoolT;
876// fn ht_nxt_color_sensor_measure_rgb(port: SensorPort, val: *mut rgb_raw_t) -> BoolT;
877// fn nxt_temp_sensor_measure(port: SensorPort, temp: *mut f32) -> BoolT;
878
879pub fn ultrasonic_sensor_get_last_reading_nxt(port: SensorPort) -> i16 {
880    unsafe { nxt_ultrasonic_sensor_get_last_reading(port) }
881}
882
883pub fn ultrasonic_sensor_did_reset_nxt(port: SensorPort) -> bool {
884    unsafe { nxt_ultrasonic_sensor_did_reset(port) != 0 }
885}
886
887pub fn ultrasonic_sensor_request_read_nxt(port: SensorPort) -> bool {
888    unsafe { nxt_ultrasonic_sensor_request_read(port) != 0 }
889}
890
891pub fn ultrasonic_sensor_request_reset_nxt(port: SensorPort) -> bool {
892    unsafe { nxt_ultrasonic_sensor_request_reset(port) != 0 }
893}
894
895pub fn ultrasonic_sensor_get_distance_nxt(port: SensorPort) -> i16 {
896    let mut d = 0;
897    unsafe {
898        nxt_ultrasonic_sensor_get_distance(port, &mut d);
899    }
900    d
901}
902
903// fn ev3_speaker_set_volume(volume: u8) -> ER;
904// fn ev3_speaker_play_tone(frequency: u16, duration: i32) -> ER;
905// fn ev3_speaker_stop() -> ER;
906
907const MAX_PATH_LEN: usize = 32;
908fn str2path(s: &str) -> [u8; MAX_PATH_LEN] {
909    let mut c_path = [0u8; MAX_PATH_LEN];
910    for (i, c) in s.bytes().take(MAX_PATH_LEN - 1).enumerate() {
911        c_path[i] = c;
912    }
913    c_path
914}
915
916pub fn memfile_load(path: &str, memfile: &mut MemFile) -> ER {
917    let path = str2path(path);
918    unsafe { ev3_memfile_load(path.as_ptr(), memfile) }
919}
920pub fn memfile_free(memfile: &mut MemFile) -> ER {
921    unsafe { ev3_memfile_free(memfile) }
922}
923pub fn file_write(path: &str, data: &[u8]) -> ER {
924    let path = str2path(path);
925    unsafe { ev3_file_write(path.as_ptr(), data.as_ptr(), data.len() as u32) }
926}
927
928pub fn led_set_color(color: LedColor) -> ER {
929    unsafe { ev3_led_set_color(color) }
930}