1use pamoja_sensors::{
22 ads1115, bme280, bmp280, ds18b20, hdc1080, ina219, ina226, opt3001, scd4x, sht3x, tmp117,
23 SensorError,
24};
25
26use crate::{read_bytes, set_last_error, PamojaStatus};
27
28pub const PAMOJA_BME280_CALIBRATION_TEMP_PRESS_LEN: usize = 26;
30
31pub const PAMOJA_BME280_CALIBRATION_HUMIDITY_LEN: usize = 7;
33
34pub const PAMOJA_BME280_MEASUREMENT_LEN: usize = 8;
36
37pub const PAMOJA_DS18B20_SCRATCHPAD_LEN: usize = 9;
39
40pub struct PamojaBme280Calibration {
46 calibration: bme280::Calibration,
47}
48
49#[repr(C)]
51#[derive(Clone, Copy, Debug, PartialEq)]
52pub struct PamojaBme280Measurement {
53 pub celsius: f32,
55 pub pascals: u32,
57 pub hectopascals: f32,
59 pub relative_humidity_percent: f32,
61}
62
63#[repr(C)]
65#[derive(Clone, Copy, Debug, PartialEq, Eq)]
66pub struct PamojaDs18b20Reading {
67 pub raw_temperature: i16,
69 pub micro_celsius: i32,
71 pub alarm_high: i8,
73 pub alarm_low: i8,
75 pub resolution_bits: u8,
77}
78
79#[repr(C)]
85#[derive(Clone, Copy, Debug, PartialEq, Eq)]
86pub struct PamojaAds1115Config {
87 pub start_conversion: u8,
89 pub mux: u8,
91 pub pga: u8,
93 pub single_shot: u8,
95 pub data_rate: u8,
97 pub window_comparator: u8,
99 pub comparator_active_high: u8,
101 pub comparator_latching: u8,
103 pub comparator_queue: u8,
105}
106
107#[no_mangle]
121pub unsafe extern "C" fn pamoja_bme280_calibration_new(
122 temp_press: *const u8,
123 temp_press_len: usize,
124 humidity: *const u8,
125 humidity_len: usize,
126 out_calibration: *mut *mut PamojaBme280Calibration,
127) -> PamojaStatus {
128 if out_calibration.is_null() {
129 set_last_error("out_calibration must not be null".to_owned());
130 return PamojaStatus::InvalidArgument;
131 }
132 let slot = &mut *out_calibration;
133 *slot = std::ptr::null_mut();
134
135 let temp_press = match read_bytes(temp_press, temp_press_len) {
136 Ok(bytes) => bytes,
137 Err(status) => return status,
138 };
139 let humidity = match read_bytes(humidity, humidity_len) {
140 Ok(bytes) => bytes,
141 Err(status) => return status,
142 };
143
144 let Ok(temp_press) =
145 <[u8; PAMOJA_BME280_CALIBRATION_TEMP_PRESS_LEN]>::try_from(&temp_press[..])
146 else {
147 return wrong_length("temperature and pressure calibration", 26);
148 };
149 let Ok(humidity) = <[u8; PAMOJA_BME280_CALIBRATION_HUMIDITY_LEN]>::try_from(&humidity[..])
150 else {
151 return wrong_length("humidity calibration", 7);
152 };
153
154 *slot = Box::into_raw(Box::new(PamojaBme280Calibration {
155 calibration: bme280::Calibration::from_registers(&temp_press, &humidity),
156 }));
157 PamojaStatus::Ok
158}
159
160#[no_mangle]
174pub unsafe extern "C" fn pamoja_bme280_compensate(
175 calibration: *const PamojaBme280Calibration,
176 measurement: *const u8,
177 measurement_len: usize,
178 out_measurement: *mut PamojaBme280Measurement,
179) -> PamojaStatus {
180 if calibration.is_null() || out_measurement.is_null() {
181 set_last_error("calibration and out_measurement must not be null".to_owned());
182 return PamojaStatus::InvalidArgument;
183 }
184 let measurement = match read_bytes(measurement, measurement_len) {
185 Ok(bytes) => bytes,
186 Err(status) => return status,
187 };
188 let Ok(registers) = <[u8; PAMOJA_BME280_MEASUREMENT_LEN]>::try_from(&measurement[..]) else {
189 return wrong_length("measurement", 8);
190 };
191
192 let reading = (*calibration)
193 .calibration
194 .compensate(&bme280::RawMeasurement::from_registers(®isters));
195 *out_measurement = PamojaBme280Measurement {
196 celsius: reading.celsius(),
197 pascals: reading.pascals(),
198 hectopascals: reading.hectopascals(),
199 relative_humidity_percent: reading.relative_humidity_percent(),
200 };
201 PamojaStatus::Ok
202}
203
204#[no_mangle]
213pub unsafe extern "C" fn pamoja_bme280_calibration_free(calibration: *mut PamojaBme280Calibration) {
214 if !calibration.is_null() {
215 drop(Box::from_raw(calibration));
216 }
217}
218
219pub const PAMOJA_BME280_I2C_ADDRESS_PRIMARY: u8 = 0x76;
221
222pub const PAMOJA_BME280_I2C_ADDRESS_SECONDARY: u8 = 0x77;
224
225pub const PAMOJA_BME280_CHIP_ID: u8 = 0x60;
227
228pub const PAMOJA_BME280_RESET_WORD: u8 = 0xB6;
230
231pub const PAMOJA_BME280_STARTUP_MICROS: u32 = 2_000;
233
234pub const PAMOJA_BME280_REGISTER_CHIP_ID: u8 = 0xD0;
236
237pub const PAMOJA_BME280_REGISTER_RESET: u8 = 0xE0;
239
240pub const PAMOJA_BME280_REGISTER_CALIB_TEMP_PRESS: u8 = 0x88;
242
243pub const PAMOJA_BME280_REGISTER_CALIB_HUMIDITY: u8 = 0xE1;
245
246pub const PAMOJA_BME280_REGISTER_DATA: u8 = 0xF7;
248
249pub const PAMOJA_BME280_REGISTER_CTRL_HUM: u8 = 0xF2;
251
252pub const PAMOJA_BME280_REGISTER_STATUS: u8 = 0xF3;
254
255pub const PAMOJA_BME280_REGISTER_CTRL_MEAS: u8 = 0xF4;
257
258pub const PAMOJA_BME280_REGISTER_CONFIG: u8 = 0xF5;
260
261const _: () = assert!(PAMOJA_BME280_I2C_ADDRESS_PRIMARY == bme280::I2C_ADDRESS_PRIMARY);
262const _: () = assert!(PAMOJA_BME280_I2C_ADDRESS_SECONDARY == bme280::I2C_ADDRESS_SECONDARY);
263const _: () = assert!(PAMOJA_BME280_CHIP_ID == bme280::CHIP_ID);
264const _: () = assert!(PAMOJA_BME280_RESET_WORD == bme280::RESET_WORD);
265const _: () = assert!(PAMOJA_BME280_STARTUP_MICROS == bme280::STARTUP_MICROS);
266const _: () = assert!(PAMOJA_BME280_REGISTER_CHIP_ID == bme280::register::CHIP_ID);
267const _: () = assert!(PAMOJA_BME280_REGISTER_RESET == bme280::register::RESET);
268const _: () =
269 assert!(PAMOJA_BME280_REGISTER_CALIB_TEMP_PRESS == bme280::register::CALIB_TEMP_PRESS);
270const _: () = assert!(PAMOJA_BME280_REGISTER_CALIB_HUMIDITY == bme280::register::CALIB_HUMIDITY);
271const _: () = assert!(PAMOJA_BME280_REGISTER_DATA == bme280::register::DATA);
272const _: () = assert!(PAMOJA_BME280_REGISTER_CTRL_HUM == bme280::register::CTRL_HUM);
273const _: () = assert!(PAMOJA_BME280_REGISTER_STATUS == bme280::register::STATUS);
274const _: () = assert!(PAMOJA_BME280_REGISTER_CTRL_MEAS == bme280::register::CTRL_MEAS);
275const _: () = assert!(PAMOJA_BME280_REGISTER_CONFIG == bme280::register::CONFIG);
276
277#[repr(C)]
279#[derive(Clone, Copy, Debug, PartialEq, Eq)]
280pub struct PamojaBme280CtrlMeas {
281 pub temperature: u8,
283 pub pressure: u8,
285 pub mode: u8,
287}
288
289#[repr(C)]
291#[derive(Clone, Copy, Debug, PartialEq, Eq)]
292pub struct PamojaBme280Config {
293 pub standby: u8,
295 pub filter: u8,
297 pub spi_3wire: u8,
299}
300
301#[no_mangle]
311pub extern "C" fn pamoja_bme280_measuring(status: u8) -> bool {
312 bme280::measuring(status)
313}
314
315#[no_mangle]
325pub extern "C" fn pamoja_bme280_image_updating(status: u8) -> bool {
326 bme280::image_updating(status)
327}
328
329#[no_mangle]
339pub extern "C" fn pamoja_bme280_ctrl_meas_bits(config: PamojaBme280CtrlMeas) -> u8 {
340 bme280::CtrlMeas::from(config).bits()
341}
342
343#[no_mangle]
359pub unsafe extern "C" fn pamoja_bme280_ctrl_meas_from_bits(
360 bits: u8,
361 out_config: *mut PamojaBme280CtrlMeas,
362) -> PamojaStatus {
363 if out_config.is_null() {
364 set_last_error("out_config must not be null".to_owned());
365 return PamojaStatus::InvalidArgument;
366 }
367 *out_config = bme280::CtrlMeas::from_bits(bits).into();
368 PamojaStatus::Ok
369}
370
371#[no_mangle]
381pub extern "C" fn pamoja_bme280_ctrl_hum_bits(humidity: u8) -> u8 {
382 bme280::CtrlHum {
383 humidity: bme280::Oversampling::from_code(humidity),
384 }
385 .bits()
386}
387
388#[no_mangle]
398pub extern "C" fn pamoja_bme280_ctrl_hum_from_bits(bits: u8) -> u8 {
399 bme280::CtrlHum::from_bits(bits).humidity.code()
400}
401
402#[no_mangle]
412pub extern "C" fn pamoja_bme280_config_bits(config: PamojaBme280Config) -> u8 {
413 bme280::Config::from(config).bits()
414}
415
416#[no_mangle]
432pub unsafe extern "C" fn pamoja_bme280_config_from_bits(
433 bits: u8,
434 out_config: *mut PamojaBme280Config,
435) -> PamojaStatus {
436 if out_config.is_null() {
437 set_last_error("out_config must not be null".to_owned());
438 return PamojaStatus::InvalidArgument;
439 }
440 *out_config = bme280::Config::from_bits(bits).into();
441 PamojaStatus::Ok
442}
443
444#[no_mangle]
454pub extern "C" fn pamoja_bme280_oversampling_factor(code: u8) -> u8 {
455 bme280::Oversampling::from_code(code).factor()
456}
457
458#[no_mangle]
468pub extern "C" fn pamoja_bme280_standby_micros(code: u8) -> u32 {
469 bme280::Standby::from_code(code).microseconds()
470}
471
472#[no_mangle]
482pub extern "C" fn pamoja_bme280_filter_coefficient(code: u8) -> u8 {
483 bme280::Filter::from_code(code).coefficient()
484}
485
486#[no_mangle]
499pub extern "C" fn pamoja_bme280_max_measurement_micros(
500 temperature: u8,
501 pressure: u8,
502 humidity: u8,
503) -> u32 {
504 bme280::max_measurement_micros(
505 bme280::Oversampling::from_code(temperature),
506 bme280::Oversampling::from_code(pressure),
507 bme280::Oversampling::from_code(humidity),
508 )
509}
510
511#[no_mangle]
523pub extern "C" fn pamoja_bme280_typical_measurement_micros(
524 temperature: u8,
525 pressure: u8,
526 humidity: u8,
527) -> u32 {
528 bme280::typical_measurement_micros(
529 bme280::Oversampling::from_code(temperature),
530 bme280::Oversampling::from_code(pressure),
531 bme280::Oversampling::from_code(humidity),
532 )
533}
534
535#[no_mangle]
548pub unsafe extern "C" fn pamoja_ds18b20_parse_scratchpad(
549 bytes: *const u8,
550 bytes_len: usize,
551 out_reading: *mut PamojaDs18b20Reading,
552) -> PamojaStatus {
553 if out_reading.is_null() {
554 set_last_error("out_reading must not be null".to_owned());
555 return PamojaStatus::InvalidArgument;
556 }
557 let bytes = match read_bytes(bytes, bytes_len) {
558 Ok(bytes) => bytes,
559 Err(status) => return status,
560 };
561 let Ok(scratchpad) = <[u8; PAMOJA_DS18B20_SCRATCHPAD_LEN]>::try_from(&bytes[..]) else {
562 return wrong_length("scratchpad", 9);
563 };
564
565 match ds18b20::Scratchpad::parse(&scratchpad) {
566 Ok(reading) => {
567 *out_reading = reading.into();
568 PamojaStatus::Ok
569 }
570 Err(error) => failed(error),
571 }
572}
573
574#[no_mangle]
594pub unsafe extern "C" fn pamoja_ds18b20_parse_w1_slave(
595 text: *const std::os::raw::c_char,
596 out_reading: *mut PamojaDs18b20Reading,
597) -> PamojaStatus {
598 if out_reading.is_null() {
599 set_last_error("out_reading must not be null".to_owned());
600 return PamojaStatus::InvalidArgument;
601 }
602 let Some(text) = crate::read_str(text, "text") else {
603 return PamojaStatus::InvalidArgument;
604 };
605 match ds18b20::parse_w1_slave(text) {
606 Ok(reading) => {
607 *out_reading = reading.into();
608 PamojaStatus::Ok
609 }
610 Err(error) => failed(error),
611 }
612}
613
614#[no_mangle]
636pub unsafe extern "C" fn pamoja_ds18b20_w1_slave_text(
637 bytes: *const u8,
638 len: usize,
639 out_text: *mut *mut crate::PamojaString,
640) -> PamojaStatus {
641 if out_text.is_null() {
642 set_last_error("out_text must not be null".to_owned());
643 return PamojaStatus::InvalidArgument;
644 }
645 *out_text = std::ptr::null_mut();
646 let bytes = match read_bytes(bytes, len) {
647 Ok(bytes) => bytes,
648 Err(status) => return status,
649 };
650 let Ok(scratchpad) = <[u8; PAMOJA_DS18B20_SCRATCHPAD_LEN]>::try_from(&bytes[..]) else {
651 return wrong_length("scratchpad", 9);
652 };
653 match ds18b20::Scratchpad::parse(&scratchpad) {
654 Ok(scratchpad) => {
655 *out_text = crate::PamojaString::into_raw(ds18b20::w1_slave_text(&scratchpad));
656 PamojaStatus::Ok
657 }
658 Err(error) => failed(error),
659 }
660}
661
662#[no_mangle]
676pub unsafe extern "C" fn pamoja_ds18b20_build_scratchpad(
677 celsius: f32,
678 bits: u8,
679 alarm_high: i8,
680 alarm_low: i8,
681 out_bytes: *mut u8,
682) -> PamojaStatus {
683 if out_bytes.is_null() {
684 set_last_error("out_bytes must not be null".to_owned());
685 return PamojaStatus::InvalidArgument;
686 }
687 let Some(resolution) = resolution(bits) else {
688 return bad_resolution();
689 };
690 let raw = ds18b20::temperature_from_celsius(celsius, resolution);
691 let scratchpad = ds18b20::Scratchpad::new(raw, resolution, alarm_high, alarm_low);
692 core::ptr::copy_nonoverlapping(scratchpad.to_bytes().as_ptr(), out_bytes, 9);
693 PamojaStatus::Ok
694}
695
696#[no_mangle]
707pub unsafe extern "C" fn pamoja_ds18b20_crc8(data: *const u8, data_len: usize) -> u8 {
708 match read_bytes(data, data_len) {
709 Ok(data) => ds18b20::crc8(&data),
710 Err(_) => 0,
711 }
712}
713
714#[no_mangle]
720pub extern "C" fn pamoja_ds18b20_micro_celsius(raw: i16) -> i32 {
721 ds18b20::temperature_to_micro_celsius(raw)
722}
723
724#[no_mangle]
730pub extern "C" fn pamoja_ds18b20_celsius(raw: i16) -> f32 {
731 ds18b20::temperature_to_celsius(raw)
732}
733
734#[no_mangle]
745pub unsafe extern "C" fn pamoja_ds18b20_config_byte(bits: u8, out_byte: *mut u8) -> PamojaStatus {
746 if out_byte.is_null() {
747 set_last_error("out_byte must not be null".to_owned());
748 return PamojaStatus::InvalidArgument;
749 }
750 match resolution(bits) {
751 Some(resolution) => {
752 *out_byte = resolution.config_byte();
753 PamojaStatus::Ok
754 }
755 None => bad_resolution(),
756 }
757}
758
759#[no_mangle]
766pub extern "C" fn pamoja_ds18b20_resolution_bits(config_byte: u8) -> u8 {
767 ds18b20::Resolution::from_config_byte(config_byte).bits()
768}
769
770#[no_mangle]
781pub unsafe extern "C" fn pamoja_ds18b20_step_micro_celsius(
782 bits: u8,
783 out_micro_celsius: *mut u32,
784) -> PamojaStatus {
785 if out_micro_celsius.is_null() {
786 set_last_error("out_micro_celsius must not be null".to_owned());
787 return PamojaStatus::InvalidArgument;
788 }
789 match resolution(bits) {
790 Some(resolution) => {
791 *out_micro_celsius = resolution.step_micro_celsius();
792 PamojaStatus::Ok
793 }
794 None => bad_resolution(),
795 }
796}
797
798#[no_mangle]
810pub unsafe extern "C" fn pamoja_ds18b20_max_conversion_micros(
811 bits: u8,
812 out_micros: *mut u32,
813) -> PamojaStatus {
814 if out_micros.is_null() {
815 set_last_error("out_micros must not be null".to_owned());
816 return PamojaStatus::InvalidArgument;
817 }
818 match resolution(bits) {
819 Some(resolution) => {
820 *out_micros = resolution.max_conversion_micros();
821 PamojaStatus::Ok
822 }
823 None => bad_resolution(),
824 }
825}
826
827#[no_mangle]
833pub extern "C" fn pamoja_ina219_calibration(
834 current_lsb_microamps: u32,
835 shunt_milliohms: u32,
836) -> u16 {
837 ina219::calibration(current_lsb_microamps, shunt_milliohms)
838}
839
840#[no_mangle]
846pub extern "C" fn pamoja_ina219_minimum_current_lsb_microamps(max_expected_microamps: u32) -> u32 {
847 ina219::minimum_current_lsb_microamps(max_expected_microamps)
848}
849
850#[no_mangle]
856pub extern "C" fn pamoja_ina219_shunt_register(microvolts: i32) -> i16 {
857 ina219::shunt_register(microvolts)
858}
859
860#[no_mangle]
866pub extern "C" fn pamoja_ina219_bus_register(millivolts: u32) -> u16 {
867 ina219::bus_register(millivolts)
868}
869
870#[no_mangle]
876pub extern "C" fn pamoja_ina219_current_register(
877 microamps: i32,
878 current_lsb_microamps: u32,
879) -> i16 {
880 ina219::current_register(microamps, current_lsb_microamps)
881}
882
883#[no_mangle]
889pub extern "C" fn pamoja_ina219_power_register(microwatts: u32, current_lsb_microamps: u32) -> u16 {
890 ina219::power_register(microwatts, current_lsb_microamps)
891}
892
893#[no_mangle]
899pub extern "C" fn pamoja_ina219_shunt_microvolts(raw: i16) -> i32 {
900 ina219::shunt_microvolts(raw)
901}
902
903#[no_mangle]
909pub extern "C" fn pamoja_ina219_bus_millivolts(raw: u16) -> u32 {
910 ina219::bus_millivolts(raw)
911}
912
913#[no_mangle]
919pub extern "C" fn pamoja_ina219_conversion_ready(raw: u16) -> bool {
920 ina219::conversion_ready(raw)
921}
922
923#[no_mangle]
930pub extern "C" fn pamoja_ina219_math_overflow(raw: u16) -> bool {
931 ina219::math_overflow(raw)
932}
933
934#[no_mangle]
940pub extern "C" fn pamoja_ina219_current_microamps(raw: i16, current_lsb_microamps: u32) -> i32 {
941 ina219::current_microamps(raw, current_lsb_microamps)
942}
943
944#[no_mangle]
950pub extern "C" fn pamoja_ina219_power_microwatts(raw: u16, current_lsb_microamps: u32) -> u32 {
951 ina219::power_microwatts(raw, current_lsb_microamps)
952}
953
954pub const PAMOJA_INA219_BASE_ADDRESS: u8 = 0x40;
956
957#[no_mangle]
975pub unsafe extern "C" fn pamoja_ina219_address(
976 a1: u8,
977 a0: u8,
978 out_address: *mut u8,
979) -> PamojaStatus {
980 if out_address.is_null() {
981 set_last_error("out_address must not be null".to_owned());
982 return PamojaStatus::InvalidArgument;
983 }
984 let (Some(a1), Some(a0)) = (address_pin(a1), address_pin(a0)) else {
985 set_last_error(
986 "an INA219 address pin must be tied to GND, VS+, SDA, or SCL: code 0 to 3".to_owned(),
987 );
988 return PamojaStatus::InvalidArgument;
989 };
990 *out_address = ina219::address(a1, a0);
991 PamojaStatus::Ok
992}
993
994pub const PAMOJA_INA219_CONFIG_RESET: u16 = 0x399F;
997
998pub const PAMOJA_INA219_REGISTER_CONFIGURATION: u8 = 0x00;
1000
1001pub const PAMOJA_INA219_REGISTER_SHUNT_VOLTAGE: u8 = 0x01;
1003
1004pub const PAMOJA_INA219_REGISTER_BUS_VOLTAGE: u8 = 0x02;
1006
1007pub const PAMOJA_INA219_REGISTER_POWER: u8 = 0x03;
1009
1010pub const PAMOJA_INA219_REGISTER_CURRENT: u8 = 0x04;
1012
1013pub const PAMOJA_INA219_REGISTER_CALIBRATION: u8 = 0x05;
1015
1016const _: () = assert!(PAMOJA_INA219_BASE_ADDRESS == ina219::BASE_ADDRESS);
1017const _: () = assert!(PAMOJA_INA219_CONFIG_RESET == ina219::CONFIG_RESET);
1018const _: () = assert!(PAMOJA_INA219_REGISTER_CONFIGURATION == ina219::register::CONFIGURATION);
1019const _: () = assert!(PAMOJA_INA219_REGISTER_SHUNT_VOLTAGE == ina219::register::SHUNT_VOLTAGE);
1020const _: () = assert!(PAMOJA_INA219_REGISTER_BUS_VOLTAGE == ina219::register::BUS_VOLTAGE);
1021const _: () = assert!(PAMOJA_INA219_REGISTER_POWER == ina219::register::POWER);
1022const _: () = assert!(PAMOJA_INA219_REGISTER_CURRENT == ina219::register::CURRENT);
1023const _: () = assert!(PAMOJA_INA219_REGISTER_CALIBRATION == ina219::register::CALIBRATION);
1024
1025#[repr(C)]
1028#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1029pub struct PamojaIna219Config {
1030 pub reset: u8,
1032 pub bus_range: u8,
1034 pub gain: u8,
1036 pub bus_adc: u8,
1039 pub shunt_adc: u8,
1041 pub mode: u8,
1043}
1044
1045#[no_mangle]
1055pub extern "C" fn pamoja_ina219_config_bits(config: PamojaIna219Config) -> u16 {
1056 ina219::Configuration::from(config).bits()
1057}
1058
1059#[no_mangle]
1074pub unsafe extern "C" fn pamoja_ina219_config_from_bits(
1075 bits: u16,
1076 out_config: *mut PamojaIna219Config,
1077) -> PamojaStatus {
1078 if out_config.is_null() {
1079 set_last_error("out_config must not be null".to_owned());
1080 return PamojaStatus::InvalidArgument;
1081 }
1082 *out_config = ina219::Configuration::from_bits(bits).into();
1083 PamojaStatus::Ok
1084}
1085
1086#[no_mangle]
1097pub extern "C" fn pamoja_ina219_conversion_micros(config: PamojaIna219Config) -> u32 {
1098 ina219::Configuration::from(config).conversion_micros()
1099}
1100
1101#[no_mangle]
1111pub extern "C" fn pamoja_ina219_adc_conversion_micros(code: u8) -> u32 {
1112 ina219::Adc::from_code(code).conversion_micros()
1113}
1114
1115#[no_mangle]
1125pub extern "C" fn pamoja_ina219_gain_range_millivolts(code: u8) -> u16 {
1126 ina219::Gain::from_code(code).range_millivolts()
1127}
1128
1129pub const PAMOJA_ADS1115_ADDRESS_GND: u8 = 0x48;
1131
1132pub const PAMOJA_ADS1115_ADDRESS_VDD: u8 = 0x49;
1134
1135pub const PAMOJA_ADS1115_ADDRESS_SDA: u8 = 0x4A;
1137
1138pub const PAMOJA_ADS1115_ADDRESS_SCL: u8 = 0x4B;
1140
1141pub const PAMOJA_ADS1115_REGISTER_CONVERSION: u8 = 0x00;
1143
1144pub const PAMOJA_ADS1115_REGISTER_CONFIG: u8 = 0x01;
1146
1147pub const PAMOJA_ADS1115_REGISTER_LO_THRESH: u8 = 0x02;
1149
1150pub const PAMOJA_ADS1115_REGISTER_HI_THRESH: u8 = 0x03;
1152
1153pub const PAMOJA_ADS1115_CONFIG_RESET: u16 = 0x8583;
1155
1156const _: () = assert!(PAMOJA_ADS1115_ADDRESS_GND == ads1115::address::GND);
1157const _: () = assert!(PAMOJA_ADS1115_ADDRESS_VDD == ads1115::address::VDD);
1158const _: () = assert!(PAMOJA_ADS1115_ADDRESS_SDA == ads1115::address::SDA);
1159const _: () = assert!(PAMOJA_ADS1115_ADDRESS_SCL == ads1115::address::SCL);
1160const _: () = assert!(PAMOJA_ADS1115_REGISTER_CONVERSION == ads1115::register::CONVERSION);
1161const _: () = assert!(PAMOJA_ADS1115_REGISTER_CONFIG == ads1115::register::CONFIG);
1162const _: () = assert!(PAMOJA_ADS1115_REGISTER_LO_THRESH == ads1115::register::LO_THRESH);
1163const _: () = assert!(PAMOJA_ADS1115_REGISTER_HI_THRESH == ads1115::register::HI_THRESH);
1164const _: () = assert!(PAMOJA_ADS1115_CONFIG_RESET == ads1115::CONFIG_RESET);
1165
1166#[no_mangle]
1177pub extern "C" fn pamoja_ads1115_conversion_micros(data_rate: u8) -> u32 {
1178 ads1115::conversion_micros(ads1115::DataRate::from_code(data_rate))
1179}
1180
1181#[no_mangle]
1187pub extern "C" fn pamoja_ads1115_config_bits(config: PamojaAds1115Config) -> u16 {
1188 ads1115::Config::from(config).bits()
1189}
1190
1191#[no_mangle]
1202pub unsafe extern "C" fn pamoja_ads1115_config_from_bits(
1203 bits: u16,
1204 out_config: *mut PamojaAds1115Config,
1205) -> PamojaStatus {
1206 if out_config.is_null() {
1207 set_last_error("out_config must not be null".to_owned());
1208 return PamojaStatus::InvalidArgument;
1209 }
1210 *out_config = ads1115::Config::from_bits(bits).into();
1211 PamojaStatus::Ok
1212}
1213
1214#[no_mangle]
1220pub extern "C" fn pamoja_ads1115_full_scale_microvolts(pga: u8) -> u32 {
1221 ads1115::Pga::from_code(pga).full_scale_microvolts()
1222}
1223
1224#[no_mangle]
1230pub extern "C" fn pamoja_ads1115_samples_per_second(data_rate: u8) -> u16 {
1231 ads1115::DataRate::from_code(data_rate).samples_per_second()
1232}
1233
1234#[no_mangle]
1240pub extern "C" fn pamoja_ads1115_to_nanovolts(pga: u8, raw: i16) -> i64 {
1241 ads1115::to_nanovolts(ads1115::Pga::from_code(pga), raw)
1242}
1243
1244#[no_mangle]
1250pub extern "C" fn pamoja_ads1115_to_volts(pga: u8, raw: i16) -> f32 {
1251 ads1115::to_volts(ads1115::Pga::from_code(pga), raw)
1252}
1253
1254pub const PAMOJA_BMP280_CALIBRATION_LEN: usize = 24;
1256
1257pub const PAMOJA_BMP280_DATA_LEN: usize = 6;
1259
1260pub const PAMOJA_BMP280_I2C_ADDRESS_PRIMARY: u8 = 0x76;
1262
1263pub const PAMOJA_BMP280_I2C_ADDRESS_SECONDARY: u8 = 0x77;
1265
1266pub const PAMOJA_BMP280_CHIP_ID: u8 = 0x58;
1268
1269pub const PAMOJA_BMP280_RESET_WORD: u8 = 0xB6;
1271
1272pub const PAMOJA_BMP280_SKIPPED_OUTPUT: u32 = 0x80000;
1274
1275pub const PAMOJA_BMP280_REGISTER_CALIBRATION: u8 = 0x88;
1277
1278pub const PAMOJA_BMP280_REGISTER_CHIP_ID: u8 = 0xD0;
1280
1281pub const PAMOJA_BMP280_REGISTER_RESET: u8 = 0xE0;
1283
1284pub const PAMOJA_BMP280_REGISTER_STATUS: u8 = 0xF3;
1286
1287pub const PAMOJA_BMP280_REGISTER_CTRL_MEAS: u8 = 0xF4;
1289
1290pub const PAMOJA_BMP280_REGISTER_CONFIG: u8 = 0xF5;
1292
1293pub const PAMOJA_BMP280_REGISTER_DATA: u8 = 0xF7;
1295
1296const _: () = assert!(PAMOJA_BMP280_CALIBRATION_LEN == bmp280::CALIBRATION_LEN);
1299const _: () = assert!(PAMOJA_BMP280_DATA_LEN == bmp280::DATA_LEN);
1300const _: () = assert!(PAMOJA_BMP280_I2C_ADDRESS_PRIMARY == bmp280::I2C_ADDRESS_PRIMARY);
1301const _: () = assert!(PAMOJA_BMP280_I2C_ADDRESS_SECONDARY == bmp280::I2C_ADDRESS_SECONDARY);
1302const _: () = assert!(PAMOJA_BMP280_CHIP_ID == bmp280::CHIP_ID);
1303const _: () = assert!(PAMOJA_BMP280_RESET_WORD == bmp280::RESET_WORD);
1304const _: () = assert!(PAMOJA_BMP280_SKIPPED_OUTPUT == bmp280::SKIPPED_OUTPUT);
1305const _: () = assert!(PAMOJA_BMP280_REGISTER_CALIBRATION == bmp280::register::CALIBRATION);
1306const _: () = assert!(PAMOJA_BMP280_REGISTER_CHIP_ID == bmp280::register::CHIP_ID);
1307const _: () = assert!(PAMOJA_BMP280_REGISTER_RESET == bmp280::register::RESET);
1308const _: () = assert!(PAMOJA_BMP280_REGISTER_STATUS == bmp280::register::STATUS);
1309const _: () = assert!(PAMOJA_BMP280_REGISTER_CTRL_MEAS == bmp280::register::CTRL_MEAS);
1310const _: () = assert!(PAMOJA_BMP280_REGISTER_CONFIG == bmp280::register::CONFIG);
1311const _: () = assert!(PAMOJA_BMP280_REGISTER_DATA == bmp280::register::DATA);
1312
1313pub struct PamojaBmp280Calibration {
1319 calibration: bmp280::Calibration,
1320}
1321
1322#[repr(C)]
1324#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1325pub struct PamojaBmp280Coefficients {
1326 pub dig_t1: u16,
1328 pub dig_t2: i16,
1330 pub dig_t3: i16,
1332 pub dig_p1: u16,
1334 pub dig_p2: i16,
1336 pub dig_p3: i16,
1338 pub dig_p4: i16,
1340 pub dig_p5: i16,
1342 pub dig_p6: i16,
1344 pub dig_p7: i16,
1346 pub dig_p8: i16,
1348 pub dig_p9: i16,
1350}
1351
1352#[repr(C)]
1354#[derive(Clone, Copy, Debug, PartialEq)]
1355pub struct PamojaBmp280Reading {
1356 pub celsius: f32,
1358 pub pascals: u32,
1360 pub hectopascals: f32,
1362}
1363
1364#[repr(C)]
1366#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1367pub struct PamojaBmp280Measurement {
1368 pub pressure: u32,
1370 pub temperature: u32,
1372 pub pressure_skipped: u8,
1374 pub temperature_skipped: u8,
1376}
1377
1378#[repr(C)]
1380#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1381pub struct PamojaBmp280CtrlMeas {
1382 pub temperature: u8,
1384 pub pressure: u8,
1386 pub mode: u8,
1388}
1389
1390#[repr(C)]
1392#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1393pub struct PamojaBmp280Config {
1394 pub standby: u8,
1396 pub filter: u8,
1398 pub spi_3wire: u8,
1400}
1401
1402#[no_mangle]
1415pub unsafe extern "C" fn pamoja_bmp280_calibration_new(
1416 bytes: *const u8,
1417 bytes_len: usize,
1418 out_calibration: *mut *mut PamojaBmp280Calibration,
1419) -> PamojaStatus {
1420 if out_calibration.is_null() {
1421 set_last_error("out_calibration must not be null".to_owned());
1422 return PamojaStatus::InvalidArgument;
1423 }
1424 let slot = &mut *out_calibration;
1425 *slot = std::ptr::null_mut();
1426
1427 let bytes = match read_bytes(bytes, bytes_len) {
1428 Ok(bytes) => bytes,
1429 Err(status) => return status,
1430 };
1431 let Ok(registers) = <[u8; PAMOJA_BMP280_CALIBRATION_LEN]>::try_from(&bytes[..]) else {
1432 return wrong_length("calibration", PAMOJA_BMP280_CALIBRATION_LEN);
1433 };
1434
1435 *slot = Box::into_raw(Box::new(PamojaBmp280Calibration {
1436 calibration: bmp280::Calibration::parse(®isters),
1437 }));
1438 PamojaStatus::Ok
1439}
1440
1441#[no_mangle]
1455pub unsafe extern "C" fn pamoja_bmp280_compensate(
1456 calibration: *const PamojaBmp280Calibration,
1457 measurement: *const u8,
1458 measurement_len: usize,
1459 out_reading: *mut PamojaBmp280Reading,
1460) -> PamojaStatus {
1461 if calibration.is_null() || out_reading.is_null() {
1462 set_last_error("calibration and out_reading must not be null".to_owned());
1463 return PamojaStatus::InvalidArgument;
1464 }
1465 let measurement = match read_bytes(measurement, measurement_len) {
1466 Ok(bytes) => bytes,
1467 Err(status) => return status,
1468 };
1469 let Ok(registers) = <[u8; PAMOJA_BMP280_DATA_LEN]>::try_from(&measurement[..]) else {
1470 return wrong_length("measurement", PAMOJA_BMP280_DATA_LEN);
1471 };
1472
1473 let reading = (*calibration)
1474 .calibration
1475 .compensate(&bmp280::Measurement::parse(®isters));
1476 *out_reading = PamojaBmp280Reading {
1477 celsius: reading.celsius(),
1478 pascals: reading.pascals(),
1479 hectopascals: reading.hectopascals(),
1480 };
1481 PamojaStatus::Ok
1482}
1483
1484#[no_mangle]
1496pub unsafe extern "C" fn pamoja_bmp280_calibration_to_bytes(
1497 calibration: *const PamojaBmp280Calibration,
1498 out_bytes: *mut u8,
1499) -> PamojaStatus {
1500 if calibration.is_null() || out_bytes.is_null() {
1501 set_last_error("calibration and out_bytes must not be null".to_owned());
1502 return PamojaStatus::InvalidArgument;
1503 }
1504 let bytes = (*calibration).calibration.to_bytes();
1505 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out_bytes, PAMOJA_BMP280_CALIBRATION_LEN);
1506 PamojaStatus::Ok
1507}
1508
1509#[no_mangle]
1521pub unsafe extern "C" fn pamoja_bmp280_calibration_coefficients(
1522 calibration: *const PamojaBmp280Calibration,
1523 out_coefficients: *mut PamojaBmp280Coefficients,
1524) -> PamojaStatus {
1525 if calibration.is_null() || out_coefficients.is_null() {
1526 set_last_error("calibration and out_coefficients must not be null".to_owned());
1527 return PamojaStatus::InvalidArgument;
1528 }
1529 *out_coefficients = (*calibration).calibration.into();
1530 PamojaStatus::Ok
1531}
1532
1533#[no_mangle]
1542pub unsafe extern "C" fn pamoja_bmp280_calibration_free(calibration: *mut PamojaBmp280Calibration) {
1543 if !calibration.is_null() {
1544 drop(Box::from_raw(calibration));
1545 }
1546}
1547
1548#[no_mangle]
1560pub unsafe extern "C" fn pamoja_bmp280_parse_measurement(
1561 data: *const u8,
1562 data_len: usize,
1563 out_measurement: *mut PamojaBmp280Measurement,
1564) -> PamojaStatus {
1565 if out_measurement.is_null() {
1566 set_last_error("out_measurement must not be null".to_owned());
1567 return PamojaStatus::InvalidArgument;
1568 }
1569 let data = match read_bytes(data, data_len) {
1570 Ok(bytes) => bytes,
1571 Err(status) => return status,
1572 };
1573 let Ok(registers) = <[u8; PAMOJA_BMP280_DATA_LEN]>::try_from(&data[..]) else {
1574 return wrong_length("measurement", PAMOJA_BMP280_DATA_LEN);
1575 };
1576 *out_measurement = bmp280::Measurement::parse(®isters).into();
1577 PamojaStatus::Ok
1578}
1579
1580#[no_mangle]
1591pub unsafe extern "C" fn pamoja_bmp280_measurement_bytes(
1592 pressure: u32,
1593 temperature: u32,
1594 out_bytes: *mut u8,
1595) -> PamojaStatus {
1596 if out_bytes.is_null() {
1597 set_last_error("out_bytes must not be null".to_owned());
1598 return PamojaStatus::InvalidArgument;
1599 }
1600 let measurement = bmp280::Measurement {
1601 pressure,
1602 temperature,
1603 };
1604 let bytes = measurement.to_bytes();
1605 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out_bytes, PAMOJA_BMP280_DATA_LEN);
1606 PamojaStatus::Ok
1607}
1608
1609#[no_mangle]
1615pub extern "C" fn pamoja_bmp280_measuring(status: u8) -> bool {
1616 bmp280::measuring(status)
1617}
1618
1619#[no_mangle]
1625pub extern "C" fn pamoja_bmp280_image_updating(status: u8) -> bool {
1626 bmp280::image_updating(status)
1627}
1628
1629#[no_mangle]
1635pub extern "C" fn pamoja_bmp280_ctrl_meas_bits(config: PamojaBmp280CtrlMeas) -> u8 {
1636 bmp280::CtrlMeas::from(config).bits()
1637}
1638
1639#[no_mangle]
1650pub unsafe extern "C" fn pamoja_bmp280_ctrl_meas_from_bits(
1651 bits: u8,
1652 out_config: *mut PamojaBmp280CtrlMeas,
1653) -> PamojaStatus {
1654 if out_config.is_null() {
1655 set_last_error("out_config must not be null".to_owned());
1656 return PamojaStatus::InvalidArgument;
1657 }
1658 *out_config = bmp280::CtrlMeas::from_bits(bits).into();
1659 PamojaStatus::Ok
1660}
1661
1662#[no_mangle]
1668pub extern "C" fn pamoja_bmp280_config_bits(config: PamojaBmp280Config) -> u8 {
1669 bmp280::Config::from(config).bits()
1670}
1671
1672#[no_mangle]
1683pub unsafe extern "C" fn pamoja_bmp280_config_from_bits(
1684 bits: u8,
1685 out_config: *mut PamojaBmp280Config,
1686) -> PamojaStatus {
1687 if out_config.is_null() {
1688 set_last_error("out_config must not be null".to_owned());
1689 return PamojaStatus::InvalidArgument;
1690 }
1691 *out_config = bmp280::Config::from_bits(bits).into();
1692 PamojaStatus::Ok
1693}
1694
1695#[no_mangle]
1701pub extern "C" fn pamoja_bmp280_oversampling_factor(code: u8) -> u8 {
1702 bmp280::Oversampling::from_code(code).factor()
1703}
1704
1705#[no_mangle]
1711pub extern "C" fn pamoja_bmp280_standby_micros(code: u8) -> u32 {
1712 bmp280::Standby::from_code(code).microseconds()
1713}
1714
1715pub const PAMOJA_SHT3X_MEASUREMENT_LEN: usize = 6;
1717
1718pub const PAMOJA_SHT3X_WORD_LEN: usize = 3;
1720
1721pub const PAMOJA_SHT3X_I2C_ADDRESS_A: u8 = 0x44;
1723
1724pub const PAMOJA_SHT3X_I2C_ADDRESS_B: u8 = 0x45;
1726
1727pub const PAMOJA_SHT3X_MIN_COMMAND_GAP_MICROS: u32 = 1_000;
1729
1730pub const PAMOJA_SHT3X_STATUS_DEFAULT: u16 = 0x8010;
1732
1733pub const PAMOJA_SHT3X_COMMAND_SINGLE_SHOT_HIGH_STRETCH: u16 = 0x2C06;
1735
1736pub const PAMOJA_SHT3X_COMMAND_SINGLE_SHOT_MEDIUM_STRETCH: u16 = 0x2C0D;
1738
1739pub const PAMOJA_SHT3X_COMMAND_SINGLE_SHOT_LOW_STRETCH: u16 = 0x2C10;
1741
1742pub const PAMOJA_SHT3X_COMMAND_SINGLE_SHOT_HIGH: u16 = 0x2400;
1744
1745pub const PAMOJA_SHT3X_COMMAND_SINGLE_SHOT_MEDIUM: u16 = 0x240B;
1747
1748pub const PAMOJA_SHT3X_COMMAND_SINGLE_SHOT_LOW: u16 = 0x2416;
1750
1751pub const PAMOJA_SHT3X_COMMAND_PERIODIC_HALF_MPS_HIGH: u16 = 0x2032;
1753
1754pub const PAMOJA_SHT3X_COMMAND_PERIODIC_HALF_MPS_MEDIUM: u16 = 0x2024;
1756
1757pub const PAMOJA_SHT3X_COMMAND_PERIODIC_HALF_MPS_LOW: u16 = 0x202F;
1759
1760pub const PAMOJA_SHT3X_COMMAND_PERIODIC_ONE_MPS_HIGH: u16 = 0x2130;
1762
1763pub const PAMOJA_SHT3X_COMMAND_PERIODIC_ONE_MPS_MEDIUM: u16 = 0x2126;
1765
1766pub const PAMOJA_SHT3X_COMMAND_PERIODIC_ONE_MPS_LOW: u16 = 0x212D;
1768
1769pub const PAMOJA_SHT3X_COMMAND_PERIODIC_TWO_MPS_HIGH: u16 = 0x2236;
1771
1772pub const PAMOJA_SHT3X_COMMAND_PERIODIC_TWO_MPS_MEDIUM: u16 = 0x2220;
1774
1775pub const PAMOJA_SHT3X_COMMAND_PERIODIC_TWO_MPS_LOW: u16 = 0x222B;
1777
1778pub const PAMOJA_SHT3X_COMMAND_PERIODIC_FOUR_MPS_HIGH: u16 = 0x2334;
1780
1781pub const PAMOJA_SHT3X_COMMAND_PERIODIC_FOUR_MPS_MEDIUM: u16 = 0x2322;
1783
1784pub const PAMOJA_SHT3X_COMMAND_PERIODIC_FOUR_MPS_LOW: u16 = 0x2329;
1786
1787pub const PAMOJA_SHT3X_COMMAND_PERIODIC_TEN_MPS_HIGH: u16 = 0x2737;
1789
1790pub const PAMOJA_SHT3X_COMMAND_PERIODIC_TEN_MPS_MEDIUM: u16 = 0x2721;
1792
1793pub const PAMOJA_SHT3X_COMMAND_PERIODIC_TEN_MPS_LOW: u16 = 0x272A;
1795
1796pub const PAMOJA_SHT3X_COMMAND_PERIODIC_ART: u16 = 0x2B32;
1798
1799pub const PAMOJA_SHT3X_COMMAND_FETCH_DATA: u16 = 0xE000;
1801
1802pub const PAMOJA_SHT3X_COMMAND_BREAK: u16 = 0x3093;
1804
1805pub const PAMOJA_SHT3X_COMMAND_SOFT_RESET: u16 = 0x30A2;
1807
1808pub const PAMOJA_SHT3X_COMMAND_GENERAL_CALL_RESET: u16 = 0x0006;
1810
1811pub const PAMOJA_SHT3X_COMMAND_HEATER_ENABLE: u16 = 0x306D;
1813
1814pub const PAMOJA_SHT3X_COMMAND_HEATER_DISABLE: u16 = 0x3066;
1816
1817pub const PAMOJA_SHT3X_COMMAND_READ_STATUS: u16 = 0xF32D;
1819
1820pub const PAMOJA_SHT3X_COMMAND_CLEAR_STATUS: u16 = 0x3041;
1822
1823const _: () = assert!(PAMOJA_SHT3X_I2C_ADDRESS_A == sht3x::I2C_ADDRESS_A);
1826const _: () = assert!(PAMOJA_SHT3X_I2C_ADDRESS_B == sht3x::I2C_ADDRESS_B);
1827const _: () = assert!(PAMOJA_SHT3X_MIN_COMMAND_GAP_MICROS == sht3x::MIN_COMMAND_GAP_MICROS);
1828const _: () = assert!(PAMOJA_SHT3X_STATUS_DEFAULT == sht3x::Status::DEFAULT);
1829const _: () = assert!(
1830 PAMOJA_SHT3X_COMMAND_SINGLE_SHOT_HIGH_STRETCH == sht3x::command::SINGLE_SHOT_HIGH_STRETCH
1831);
1832const _: () = assert!(
1833 PAMOJA_SHT3X_COMMAND_SINGLE_SHOT_MEDIUM_STRETCH == sht3x::command::SINGLE_SHOT_MEDIUM_STRETCH
1834);
1835const _: () = assert!(
1836 PAMOJA_SHT3X_COMMAND_SINGLE_SHOT_LOW_STRETCH == sht3x::command::SINGLE_SHOT_LOW_STRETCH
1837);
1838const _: () = assert!(PAMOJA_SHT3X_COMMAND_SINGLE_SHOT_HIGH == sht3x::command::SINGLE_SHOT_HIGH);
1839const _: () =
1840 assert!(PAMOJA_SHT3X_COMMAND_SINGLE_SHOT_MEDIUM == sht3x::command::SINGLE_SHOT_MEDIUM);
1841const _: () = assert!(PAMOJA_SHT3X_COMMAND_SINGLE_SHOT_LOW == sht3x::command::SINGLE_SHOT_LOW);
1842const _: () =
1843 assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_HALF_MPS_HIGH == sht3x::command::PERIODIC_0_5_MPS_HIGH);
1844const _: () = assert!(
1845 PAMOJA_SHT3X_COMMAND_PERIODIC_HALF_MPS_MEDIUM == sht3x::command::PERIODIC_0_5_MPS_MEDIUM
1846);
1847const _: () =
1848 assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_HALF_MPS_LOW == sht3x::command::PERIODIC_0_5_MPS_LOW);
1849const _: () =
1850 assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_ONE_MPS_HIGH == sht3x::command::PERIODIC_1_MPS_HIGH);
1851const _: () =
1852 assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_ONE_MPS_MEDIUM == sht3x::command::PERIODIC_1_MPS_MEDIUM);
1853const _: () =
1854 assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_ONE_MPS_LOW == sht3x::command::PERIODIC_1_MPS_LOW);
1855const _: () =
1856 assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_TWO_MPS_HIGH == sht3x::command::PERIODIC_2_MPS_HIGH);
1857const _: () =
1858 assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_TWO_MPS_MEDIUM == sht3x::command::PERIODIC_2_MPS_MEDIUM);
1859const _: () =
1860 assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_TWO_MPS_LOW == sht3x::command::PERIODIC_2_MPS_LOW);
1861const _: () =
1862 assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_FOUR_MPS_HIGH == sht3x::command::PERIODIC_4_MPS_HIGH);
1863const _: () =
1864 assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_FOUR_MPS_MEDIUM == sht3x::command::PERIODIC_4_MPS_MEDIUM);
1865const _: () =
1866 assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_FOUR_MPS_LOW == sht3x::command::PERIODIC_4_MPS_LOW);
1867const _: () =
1868 assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_TEN_MPS_HIGH == sht3x::command::PERIODIC_10_MPS_HIGH);
1869const _: () =
1870 assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_TEN_MPS_MEDIUM == sht3x::command::PERIODIC_10_MPS_MEDIUM);
1871const _: () =
1872 assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_TEN_MPS_LOW == sht3x::command::PERIODIC_10_MPS_LOW);
1873const _: () = assert!(PAMOJA_SHT3X_COMMAND_PERIODIC_ART == sht3x::command::PERIODIC_ART);
1874const _: () = assert!(PAMOJA_SHT3X_COMMAND_FETCH_DATA == sht3x::command::FETCH_DATA);
1875const _: () = assert!(PAMOJA_SHT3X_COMMAND_BREAK == sht3x::command::BREAK);
1876const _: () = assert!(PAMOJA_SHT3X_COMMAND_SOFT_RESET == sht3x::command::SOFT_RESET);
1877const _: () =
1878 assert!(PAMOJA_SHT3X_COMMAND_GENERAL_CALL_RESET == sht3x::command::GENERAL_CALL_RESET);
1879const _: () = assert!(PAMOJA_SHT3X_COMMAND_HEATER_ENABLE == sht3x::command::HEATER_ENABLE);
1880const _: () = assert!(PAMOJA_SHT3X_COMMAND_HEATER_DISABLE == sht3x::command::HEATER_DISABLE);
1881const _: () = assert!(PAMOJA_SHT3X_COMMAND_READ_STATUS == sht3x::command::READ_STATUS);
1882const _: () = assert!(PAMOJA_SHT3X_COMMAND_CLEAR_STATUS == sht3x::command::CLEAR_STATUS);
1883
1884#[repr(C)]
1886#[derive(Clone, Copy, Debug, PartialEq)]
1887pub struct PamojaSht3xMeasurement {
1888 pub temperature_raw: u16,
1890 pub humidity_raw: u16,
1892 pub milli_celsius: i32,
1894 pub celsius: f32,
1896 pub milli_fahrenheit: i32,
1898 pub fahrenheit: f32,
1900 pub milli_percent: u32,
1902 pub relative_humidity: f32,
1904}
1905
1906#[repr(C)]
1908#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1909pub struct PamojaSht3xStatus {
1910 pub bits: u16,
1912 pub alert_pending: u8,
1914 pub heater_on: u8,
1916 pub humidity_tracking_alert: u8,
1918 pub temperature_tracking_alert: u8,
1920 pub reset_detected: u8,
1922 pub command_failed: u8,
1924 pub write_checksum_failed: u8,
1926}
1927
1928#[no_mangle]
1939pub unsafe extern "C" fn pamoja_sht3x_crc(data: *const u8, data_len: usize) -> u8 {
1940 match read_bytes(data, data_len) {
1941 Ok(data) => sht3x::crc(&data),
1942 Err(_) => 0,
1943 }
1944}
1945
1946#[no_mangle]
1958pub unsafe extern "C" fn pamoja_sht3x_word(
1959 frame: *const u8,
1960 frame_len: usize,
1961 out_word: *mut u16,
1962) -> PamojaStatus {
1963 if out_word.is_null() {
1964 set_last_error("out_word must not be null".to_owned());
1965 return PamojaStatus::InvalidArgument;
1966 }
1967 let frame = match read_bytes(frame, frame_len) {
1968 Ok(bytes) => bytes,
1969 Err(status) => return status,
1970 };
1971 let Ok(frame) = <[u8; PAMOJA_SHT3X_WORD_LEN]>::try_from(&frame[..]) else {
1972 return wrong_length("word frame", PAMOJA_SHT3X_WORD_LEN);
1973 };
1974 match sht3x::word(&frame) {
1975 Ok(word) => {
1976 *out_word = word;
1977 PamojaStatus::Ok
1978 }
1979 Err(error) => failed(error),
1980 }
1981}
1982
1983#[no_mangle]
1994pub unsafe extern "C" fn pamoja_sht3x_word_bytes(value: u16, out_bytes: *mut u8) -> PamojaStatus {
1995 if out_bytes.is_null() {
1996 set_last_error("out_bytes must not be null".to_owned());
1997 return PamojaStatus::InvalidArgument;
1998 }
1999 let bytes = sht3x::word_bytes(value);
2000 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out_bytes, PAMOJA_SHT3X_WORD_LEN);
2001 PamojaStatus::Ok
2002}
2003
2004#[no_mangle]
2017pub unsafe extern "C" fn pamoja_sht3x_parse_measurement(
2018 frame: *const u8,
2019 frame_len: usize,
2020 out_measurement: *mut PamojaSht3xMeasurement,
2021) -> PamojaStatus {
2022 if out_measurement.is_null() {
2023 set_last_error("out_measurement must not be null".to_owned());
2024 return PamojaStatus::InvalidArgument;
2025 }
2026 let frame = match read_bytes(frame, frame_len) {
2027 Ok(bytes) => bytes,
2028 Err(status) => return status,
2029 };
2030 let Ok(frame) = <[u8; PAMOJA_SHT3X_MEASUREMENT_LEN]>::try_from(&frame[..]) else {
2031 return wrong_length("measurement frame", PAMOJA_SHT3X_MEASUREMENT_LEN);
2032 };
2033 match sht3x::Measurement::parse(&frame) {
2034 Ok(measurement) => {
2035 *out_measurement = measurement.into();
2036 PamojaStatus::Ok
2037 }
2038 Err(error) => failed(error),
2039 }
2040}
2041
2042#[no_mangle]
2053pub unsafe extern "C" fn pamoja_sht3x_measurement_bytes(
2054 temperature_raw: u16,
2055 humidity_raw: u16,
2056 out_bytes: *mut u8,
2057) -> PamojaStatus {
2058 if out_bytes.is_null() {
2059 set_last_error("out_bytes must not be null".to_owned());
2060 return PamojaStatus::InvalidArgument;
2061 }
2062 let measurement = sht3x::Measurement {
2063 temperature_raw,
2064 humidity_raw,
2065 };
2066 let bytes = measurement.to_bytes();
2067 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out_bytes, PAMOJA_SHT3X_MEASUREMENT_LEN);
2068 PamojaStatus::Ok
2069}
2070
2071#[no_mangle]
2077pub extern "C" fn pamoja_sht3x_milli_celsius(raw: u16) -> i32 {
2078 sht3x::milli_celsius(raw)
2079}
2080
2081#[no_mangle]
2087pub extern "C" fn pamoja_sht3x_celsius(raw: u16) -> f32 {
2088 sht3x::celsius(raw)
2089}
2090
2091#[no_mangle]
2097pub extern "C" fn pamoja_sht3x_milli_fahrenheit(raw: u16) -> i32 {
2098 sht3x::milli_fahrenheit(raw)
2099}
2100
2101#[no_mangle]
2107pub extern "C" fn pamoja_sht3x_fahrenheit(raw: u16) -> f32 {
2108 sht3x::fahrenheit(raw)
2109}
2110
2111#[no_mangle]
2117pub extern "C" fn pamoja_sht3x_milli_percent(raw: u16) -> u32 {
2118 sht3x::milli_percent(raw)
2119}
2120
2121#[no_mangle]
2127pub extern "C" fn pamoja_sht3x_relative_humidity(raw: u16) -> f32 {
2128 sht3x::relative_humidity(raw)
2129}
2130
2131#[no_mangle]
2137pub extern "C" fn pamoja_sht3x_temperature_raw_from_milli_celsius(milli_celsius: i32) -> u16 {
2138 sht3x::temperature_raw_from_milli_celsius(milli_celsius)
2139}
2140
2141#[no_mangle]
2147pub extern "C" fn pamoja_sht3x_temperature_raw_from_celsius(celsius: f32) -> u16 {
2148 sht3x::temperature_raw_from_celsius(celsius)
2149}
2150
2151#[no_mangle]
2157pub extern "C" fn pamoja_sht3x_temperature_raw_from_milli_fahrenheit(milli_fahrenheit: i32) -> u16 {
2158 sht3x::temperature_raw_from_milli_fahrenheit(milli_fahrenheit)
2159}
2160
2161#[no_mangle]
2167pub extern "C" fn pamoja_sht3x_humidity_raw_from_milli_percent(milli_percent: u32) -> u16 {
2168 sht3x::humidity_raw_from_milli_percent(milli_percent)
2169}
2170
2171#[no_mangle]
2177pub extern "C" fn pamoja_sht3x_humidity_raw_from_relative_humidity(percent: f32) -> u16 {
2178 sht3x::humidity_raw_from_relative_humidity(percent)
2179}
2180
2181#[no_mangle]
2193pub unsafe extern "C" fn pamoja_sht3x_parse_status(
2194 frame: *const u8,
2195 frame_len: usize,
2196 out_status: *mut PamojaSht3xStatus,
2197) -> PamojaStatus {
2198 if out_status.is_null() {
2199 set_last_error("out_status must not be null".to_owned());
2200 return PamojaStatus::InvalidArgument;
2201 }
2202 let frame = match read_bytes(frame, frame_len) {
2203 Ok(bytes) => bytes,
2204 Err(status) => return status,
2205 };
2206 let Ok(frame) = <[u8; PAMOJA_SHT3X_WORD_LEN]>::try_from(&frame[..]) else {
2207 return wrong_length("status frame", PAMOJA_SHT3X_WORD_LEN);
2208 };
2209 match sht3x::Status::parse(&frame) {
2210 Ok(status) => {
2211 *out_status = status.into();
2212 PamojaStatus::Ok
2213 }
2214 Err(error) => failed(error),
2215 }
2216}
2217
2218#[no_mangle]
2229pub unsafe extern "C" fn pamoja_sht3x_status_from_bits(
2230 bits: u16,
2231 out_status: *mut PamojaSht3xStatus,
2232) -> PamojaStatus {
2233 if out_status.is_null() {
2234 set_last_error("out_status must not be null".to_owned());
2235 return PamojaStatus::InvalidArgument;
2236 }
2237 *out_status = sht3x::Status::from_bits(bits).into();
2238 PamojaStatus::Ok
2239}
2240
2241#[no_mangle]
2252pub unsafe extern "C" fn pamoja_sht3x_status_bytes(bits: u16, out_bytes: *mut u8) -> PamojaStatus {
2253 if out_bytes.is_null() {
2254 set_last_error("out_bytes must not be null".to_owned());
2255 return PamojaStatus::InvalidArgument;
2256 }
2257 let bytes = sht3x::Status::from_bits(bits).to_bytes();
2258 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out_bytes, PAMOJA_SHT3X_WORD_LEN);
2259 PamojaStatus::Ok
2260}
2261
2262#[no_mangle]
2273pub unsafe extern "C" fn pamoja_sht3x_single_shot(
2274 repeatability: u8,
2275 clock_stretching: bool,
2276 out_command: *mut u16,
2277) -> PamojaStatus {
2278 if out_command.is_null() {
2279 set_last_error("out_command must not be null".to_owned());
2280 return PamojaStatus::InvalidArgument;
2281 }
2282 let Some(repeatability) = repeatability_from_code(repeatability) else {
2283 return bad_repeatability();
2284 };
2285 *out_command = sht3x::single_shot(repeatability, clock_stretching);
2286 PamojaStatus::Ok
2287}
2288
2289#[no_mangle]
2300pub unsafe extern "C" fn pamoja_sht3x_periodic(
2301 repeatability: u8,
2302 rate: u8,
2303 out_command: *mut u16,
2304) -> PamojaStatus {
2305 if out_command.is_null() {
2306 set_last_error("out_command must not be null".to_owned());
2307 return PamojaStatus::InvalidArgument;
2308 }
2309 let Some(repeatability) = repeatability_from_code(repeatability) else {
2310 return bad_repeatability();
2311 };
2312 let Some(rate) = rate_from_code(rate) else {
2313 return bad_rate();
2314 };
2315 *out_command = sht3x::periodic(repeatability, rate);
2316 PamojaStatus::Ok
2317}
2318
2319#[no_mangle]
2330pub unsafe extern "C" fn pamoja_sht3x_max_measurement_micros(
2331 repeatability: u8,
2332 out_micros: *mut u32,
2333) -> PamojaStatus {
2334 if out_micros.is_null() {
2335 set_last_error("out_micros must not be null".to_owned());
2336 return PamojaStatus::InvalidArgument;
2337 }
2338 match repeatability_from_code(repeatability) {
2339 Some(repeatability) => {
2340 *out_micros = repeatability.max_measurement_micros();
2341 PamojaStatus::Ok
2342 }
2343 None => bad_repeatability(),
2344 }
2345}
2346
2347#[no_mangle]
2358pub unsafe extern "C" fn pamoja_sht3x_typical_measurement_micros(
2359 repeatability: u8,
2360 out_micros: *mut u32,
2361) -> PamojaStatus {
2362 if out_micros.is_null() {
2363 set_last_error("out_micros must not be null".to_owned());
2364 return PamojaStatus::InvalidArgument;
2365 }
2366 match repeatability_from_code(repeatability) {
2367 Some(repeatability) => {
2368 *out_micros = repeatability.typical_measurement_micros();
2369 PamojaStatus::Ok
2370 }
2371 None => bad_repeatability(),
2372 }
2373}
2374
2375#[no_mangle]
2386pub unsafe extern "C" fn pamoja_sht3x_interval_micros(
2387 rate: u8,
2388 out_micros: *mut u32,
2389) -> PamojaStatus {
2390 if out_micros.is_null() {
2391 set_last_error("out_micros must not be null".to_owned());
2392 return PamojaStatus::InvalidArgument;
2393 }
2394 match rate_from_code(rate) {
2395 Some(rate) => {
2396 *out_micros = rate.interval_micros();
2397 PamojaStatus::Ok
2398 }
2399 None => bad_rate(),
2400 }
2401}
2402
2403pub const PAMOJA_SCD4X_MEASUREMENT_LEN: usize = 9;
2405
2406pub const PAMOJA_SCD4X_WORD_LEN: usize = 3;
2408
2409pub const PAMOJA_SCD4X_COMMAND_LEN: usize = 2;
2411
2412pub const PAMOJA_SCD4X_WRITE_LEN: usize = 5;
2414
2415pub const PAMOJA_SCD4X_I2C_ADDRESS: u8 = 0x62;
2417
2418pub const PAMOJA_SCD4X_CO2_MAX_PPM: u16 = 40_000;
2420
2421pub const PAMOJA_SCD4X_DEFAULT_TEMPERATURE_OFFSET_MILLI_CELSIUS: u32 = 4_000;
2423
2424pub const PAMOJA_SCD4X_PERIODIC_MEASUREMENT_INTERVAL_MS: u32 = 5_000;
2426
2427pub const PAMOJA_SCD4X_LOW_POWER_PERIODIC_MEASUREMENT_INTERVAL_MS: u32 = 30_000;
2429
2430pub const PAMOJA_SCD4X_POWER_UP_TIME_MS: u32 = 1_000;
2432
2433pub const PAMOJA_SCD4X_FORCED_RECALIBRATION_FAILED: u16 = 0xffff;
2435
2436pub const PAMOJA_SCD4X_COMMAND_START_PERIODIC_MEASUREMENT: u16 = 0x21b1;
2438
2439pub const PAMOJA_SCD4X_COMMAND_READ_MEASUREMENT: u16 = 0xec05;
2441
2442pub const PAMOJA_SCD4X_COMMAND_STOP_PERIODIC_MEASUREMENT: u16 = 0x3f86;
2444
2445pub const PAMOJA_SCD4X_COMMAND_SET_TEMPERATURE_OFFSET: u16 = 0x241d;
2447
2448pub const PAMOJA_SCD4X_COMMAND_GET_TEMPERATURE_OFFSET: u16 = 0x2318;
2450
2451pub const PAMOJA_SCD4X_COMMAND_SET_SENSOR_ALTITUDE: u16 = 0x2427;
2453
2454pub const PAMOJA_SCD4X_COMMAND_GET_SENSOR_ALTITUDE: u16 = 0x2322;
2456
2457pub const PAMOJA_SCD4X_COMMAND_SET_AMBIENT_PRESSURE: u16 = 0xe000;
2459
2460pub const PAMOJA_SCD4X_COMMAND_PERFORM_FORCED_RECALIBRATION: u16 = 0x362f;
2462
2463pub const PAMOJA_SCD4X_COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED: u16 = 0x2416;
2465
2466pub const PAMOJA_SCD4X_COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED: u16 = 0x2313;
2468
2469pub const PAMOJA_SCD4X_COMMAND_START_LOW_POWER_PERIODIC_MEASUREMENT: u16 = 0x21ac;
2471
2472pub const PAMOJA_SCD4X_COMMAND_GET_DATA_READY_STATUS: u16 = 0xe4b8;
2474
2475pub const PAMOJA_SCD4X_COMMAND_PERSIST_SETTINGS: u16 = 0x3615;
2477
2478pub const PAMOJA_SCD4X_COMMAND_GET_SERIAL_NUMBER: u16 = 0x3682;
2480
2481pub const PAMOJA_SCD4X_COMMAND_PERFORM_SELF_TEST: u16 = 0x3639;
2483
2484pub const PAMOJA_SCD4X_COMMAND_PERFORM_FACTORY_RESET: u16 = 0x3632;
2486
2487pub const PAMOJA_SCD4X_COMMAND_REINIT: u16 = 0x3646;
2489
2490pub const PAMOJA_SCD4X_COMMAND_MEASURE_SINGLE_SHOT: u16 = 0x219d;
2492
2493pub const PAMOJA_SCD4X_COMMAND_MEASURE_SINGLE_SHOT_RHT_ONLY: u16 = 0x2196;
2495
2496pub const PAMOJA_SCD4X_COMMAND_POWER_DOWN: u16 = 0x36e0;
2498
2499pub const PAMOJA_SCD4X_COMMAND_WAKE_UP: u16 = 0x36f6;
2501
2502const _: () = assert!(PAMOJA_SCD4X_I2C_ADDRESS == scd4x::I2C_ADDRESS);
2505const _: () = assert!(PAMOJA_SCD4X_CO2_MAX_PPM == scd4x::CO2_MAX_PPM);
2506const _: () = assert!(
2507 PAMOJA_SCD4X_DEFAULT_TEMPERATURE_OFFSET_MILLI_CELSIUS
2508 == scd4x::DEFAULT_TEMPERATURE_OFFSET_MILLI_CELSIUS
2509);
2510const _: () = assert!(
2511 PAMOJA_SCD4X_PERIODIC_MEASUREMENT_INTERVAL_MS == scd4x::PERIODIC_MEASUREMENT_INTERVAL_MS
2512);
2513const _: () = assert!(
2514 PAMOJA_SCD4X_LOW_POWER_PERIODIC_MEASUREMENT_INTERVAL_MS
2515 == scd4x::LOW_POWER_PERIODIC_MEASUREMENT_INTERVAL_MS
2516);
2517const _: () = assert!(PAMOJA_SCD4X_POWER_UP_TIME_MS == scd4x::POWER_UP_TIME_MS);
2518const _: () =
2519 assert!(PAMOJA_SCD4X_FORCED_RECALIBRATION_FAILED == scd4x::FORCED_RECALIBRATION_FAILED);
2520const _: () = assert!(
2521 PAMOJA_SCD4X_COMMAND_START_PERIODIC_MEASUREMENT == scd4x::command::START_PERIODIC_MEASUREMENT
2522);
2523const _: () = assert!(PAMOJA_SCD4X_COMMAND_READ_MEASUREMENT == scd4x::command::READ_MEASUREMENT);
2524const _: () = assert!(
2525 PAMOJA_SCD4X_COMMAND_STOP_PERIODIC_MEASUREMENT == scd4x::command::STOP_PERIODIC_MEASUREMENT
2526);
2527const _: () =
2528 assert!(PAMOJA_SCD4X_COMMAND_SET_TEMPERATURE_OFFSET == scd4x::command::SET_TEMPERATURE_OFFSET);
2529const _: () =
2530 assert!(PAMOJA_SCD4X_COMMAND_GET_TEMPERATURE_OFFSET == scd4x::command::GET_TEMPERATURE_OFFSET);
2531const _: () =
2532 assert!(PAMOJA_SCD4X_COMMAND_SET_SENSOR_ALTITUDE == scd4x::command::SET_SENSOR_ALTITUDE);
2533const _: () =
2534 assert!(PAMOJA_SCD4X_COMMAND_GET_SENSOR_ALTITUDE == scd4x::command::GET_SENSOR_ALTITUDE);
2535const _: () =
2536 assert!(PAMOJA_SCD4X_COMMAND_SET_AMBIENT_PRESSURE == scd4x::command::SET_AMBIENT_PRESSURE);
2537const _: () = assert!(
2538 PAMOJA_SCD4X_COMMAND_PERFORM_FORCED_RECALIBRATION
2539 == scd4x::command::PERFORM_FORCED_RECALIBRATION
2540);
2541const _: () = assert!(
2542 PAMOJA_SCD4X_COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED
2543 == scd4x::command::SET_AUTOMATIC_SELF_CALIBRATION_ENABLED
2544);
2545const _: () = assert!(
2546 PAMOJA_SCD4X_COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED
2547 == scd4x::command::GET_AUTOMATIC_SELF_CALIBRATION_ENABLED
2548);
2549const _: () = assert!(
2550 PAMOJA_SCD4X_COMMAND_START_LOW_POWER_PERIODIC_MEASUREMENT
2551 == scd4x::command::START_LOW_POWER_PERIODIC_MEASUREMENT
2552);
2553const _: () =
2554 assert!(PAMOJA_SCD4X_COMMAND_GET_DATA_READY_STATUS == scd4x::command::GET_DATA_READY_STATUS);
2555const _: () = assert!(PAMOJA_SCD4X_COMMAND_PERSIST_SETTINGS == scd4x::command::PERSIST_SETTINGS);
2556const _: () = assert!(PAMOJA_SCD4X_COMMAND_GET_SERIAL_NUMBER == scd4x::command::GET_SERIAL_NUMBER);
2557const _: () = assert!(PAMOJA_SCD4X_COMMAND_PERFORM_SELF_TEST == scd4x::command::PERFORM_SELF_TEST);
2558const _: () =
2559 assert!(PAMOJA_SCD4X_COMMAND_PERFORM_FACTORY_RESET == scd4x::command::PERFORM_FACTORY_RESET);
2560const _: () = assert!(PAMOJA_SCD4X_COMMAND_REINIT == scd4x::command::REINIT);
2561const _: () =
2562 assert!(PAMOJA_SCD4X_COMMAND_MEASURE_SINGLE_SHOT == scd4x::command::MEASURE_SINGLE_SHOT);
2563const _: () = assert!(
2564 PAMOJA_SCD4X_COMMAND_MEASURE_SINGLE_SHOT_RHT_ONLY
2565 == scd4x::command::MEASURE_SINGLE_SHOT_RHT_ONLY
2566);
2567const _: () = assert!(PAMOJA_SCD4X_COMMAND_POWER_DOWN == scd4x::command::POWER_DOWN);
2568const _: () = assert!(PAMOJA_SCD4X_COMMAND_WAKE_UP == scd4x::command::WAKE_UP);
2569
2570#[repr(C)]
2572#[derive(Clone, Copy, Debug, PartialEq)]
2573pub struct PamojaScd4xMeasurement {
2574 pub co2_ppm: u16,
2576 pub temperature_raw: u16,
2578 pub humidity_raw: u16,
2580 pub milli_celsius: i32,
2582 pub celsius: f32,
2584 pub humidity_milli_percent: u32,
2586 pub relative_humidity_percent: f32,
2588}
2589
2590#[no_mangle]
2601pub unsafe extern "C" fn pamoja_scd4x_crc(data: *const u8, data_len: usize) -> u8 {
2602 match read_bytes(data, data_len) {
2603 Ok(data) => scd4x::crc(&data),
2604 Err(_) => 0,
2605 }
2606}
2607
2608#[no_mangle]
2620pub unsafe extern "C" fn pamoja_scd4x_word(
2621 frame: *const u8,
2622 frame_len: usize,
2623 out_word: *mut u16,
2624) -> PamojaStatus {
2625 if out_word.is_null() {
2626 set_last_error("out_word must not be null".to_owned());
2627 return PamojaStatus::InvalidArgument;
2628 }
2629 let frame = match read_bytes(frame, frame_len) {
2630 Ok(bytes) => bytes,
2631 Err(status) => return status,
2632 };
2633 let Ok(frame) = <[u8; PAMOJA_SCD4X_WORD_LEN]>::try_from(&frame[..]) else {
2634 return wrong_length("word frame", PAMOJA_SCD4X_WORD_LEN);
2635 };
2636 match scd4x::word(&frame) {
2637 Ok(word) => {
2638 *out_word = word;
2639 PamojaStatus::Ok
2640 }
2641 Err(error) => failed(error),
2642 }
2643}
2644
2645#[no_mangle]
2656pub unsafe extern "C" fn pamoja_scd4x_word_frame(value: u16, out_bytes: *mut u8) -> PamojaStatus {
2657 if out_bytes.is_null() {
2658 set_last_error("out_bytes must not be null".to_owned());
2659 return PamojaStatus::InvalidArgument;
2660 }
2661 let bytes = scd4x::word_frame(value);
2662 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out_bytes, PAMOJA_SCD4X_WORD_LEN);
2663 PamojaStatus::Ok
2664}
2665
2666#[no_mangle]
2677pub unsafe extern "C" fn pamoja_scd4x_command_frame(
2678 command: u16,
2679 out_bytes: *mut u8,
2680) -> PamojaStatus {
2681 if out_bytes.is_null() {
2682 set_last_error("out_bytes must not be null".to_owned());
2683 return PamojaStatus::InvalidArgument;
2684 }
2685 let bytes = scd4x::command_frame(command);
2686 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out_bytes, PAMOJA_SCD4X_COMMAND_LEN);
2687 PamojaStatus::Ok
2688}
2689
2690#[no_mangle]
2701pub unsafe extern "C" fn pamoja_scd4x_write_frame(
2702 command: u16,
2703 value: u16,
2704 out_bytes: *mut u8,
2705) -> PamojaStatus {
2706 if out_bytes.is_null() {
2707 set_last_error("out_bytes must not be null".to_owned());
2708 return PamojaStatus::InvalidArgument;
2709 }
2710 let bytes = scd4x::write_frame(command, value);
2711 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out_bytes, PAMOJA_SCD4X_WRITE_LEN);
2712 PamojaStatus::Ok
2713}
2714
2715#[no_mangle]
2726pub unsafe extern "C" fn pamoja_scd4x_max_duration_ms(command: u16, out_millis: *mut u16) -> bool {
2727 match scd4x::max_duration_ms(command) {
2728 Some(millis) if !out_millis.is_null() => {
2729 *out_millis = millis;
2730 true
2731 }
2732 _ => false,
2733 }
2734}
2735
2736#[no_mangle]
2742pub extern "C" fn pamoja_scd4x_allowed_during_measurement(command: u16) -> bool {
2743 scd4x::allowed_during_measurement(command)
2744}
2745
2746#[no_mangle]
2759pub unsafe extern "C" fn pamoja_scd4x_parse_measurement(
2760 frame: *const u8,
2761 frame_len: usize,
2762 out_measurement: *mut PamojaScd4xMeasurement,
2763) -> PamojaStatus {
2764 if out_measurement.is_null() {
2765 set_last_error("out_measurement must not be null".to_owned());
2766 return PamojaStatus::InvalidArgument;
2767 }
2768 let frame = match read_bytes(frame, frame_len) {
2769 Ok(bytes) => bytes,
2770 Err(status) => return status,
2771 };
2772 let Ok(frame) = <[u8; PAMOJA_SCD4X_MEASUREMENT_LEN]>::try_from(&frame[..]) else {
2773 return wrong_length("measurement frame", PAMOJA_SCD4X_MEASUREMENT_LEN);
2774 };
2775 match scd4x::Measurement::parse(&frame) {
2776 Ok(measurement) => {
2777 *out_measurement = measurement.into();
2778 PamojaStatus::Ok
2779 }
2780 Err(error) => failed(error),
2781 }
2782}
2783
2784#[no_mangle]
2795pub unsafe extern "C" fn pamoja_scd4x_measurement_from_physical(
2796 co2_ppm: u16,
2797 milli_celsius: i32,
2798 humidity_milli_percent: u32,
2799 out_measurement: *mut PamojaScd4xMeasurement,
2800) -> PamojaStatus {
2801 if out_measurement.is_null() {
2802 set_last_error("out_measurement must not be null".to_owned());
2803 return PamojaStatus::InvalidArgument;
2804 }
2805 let measurement =
2806 scd4x::Measurement::from_physical(co2_ppm, milli_celsius, humidity_milli_percent);
2807 *out_measurement = measurement.into();
2808 PamojaStatus::Ok
2809}
2810
2811#[no_mangle]
2822pub unsafe extern "C" fn pamoja_scd4x_measurement_bytes(
2823 co2_ppm: u16,
2824 temperature_raw: u16,
2825 humidity_raw: u16,
2826 out_bytes: *mut u8,
2827) -> PamojaStatus {
2828 if out_bytes.is_null() {
2829 set_last_error("out_bytes must not be null".to_owned());
2830 return PamojaStatus::InvalidArgument;
2831 }
2832 let measurement = scd4x::Measurement {
2833 co2_ppm,
2834 temperature_raw,
2835 humidity_raw,
2836 };
2837 let bytes = measurement.to_bytes();
2838 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out_bytes, PAMOJA_SCD4X_MEASUREMENT_LEN);
2839 PamojaStatus::Ok
2840}
2841
2842#[no_mangle]
2848pub extern "C" fn pamoja_scd4x_milli_celsius(raw: u16) -> i32 {
2849 scd4x::milli_celsius(raw)
2850}
2851
2852#[no_mangle]
2858pub extern "C" fn pamoja_scd4x_celsius(raw: u16) -> f32 {
2859 scd4x::celsius(raw)
2860}
2861
2862#[no_mangle]
2868pub extern "C" fn pamoja_scd4x_temperature_raw(milli_celsius: i32) -> u16 {
2869 scd4x::temperature_raw(milli_celsius)
2870}
2871
2872#[no_mangle]
2878pub extern "C" fn pamoja_scd4x_humidity_milli_percent(raw: u16) -> u32 {
2879 scd4x::humidity_milli_percent(raw)
2880}
2881
2882#[no_mangle]
2888pub extern "C" fn pamoja_scd4x_relative_humidity_percent(raw: u16) -> f32 {
2889 scd4x::relative_humidity_percent(raw)
2890}
2891
2892#[no_mangle]
2898pub extern "C" fn pamoja_scd4x_humidity_raw(milli_percent: u32) -> u16 {
2899 scd4x::humidity_raw(milli_percent)
2900}
2901
2902#[no_mangle]
2908pub extern "C" fn pamoja_scd4x_data_ready(word: u16) -> bool {
2909 scd4x::data_ready(word)
2910}
2911
2912#[no_mangle]
2919pub extern "C" fn pamoja_scd4x_temperature_offset_word(milli_celsius: u32) -> u16 {
2920 scd4x::temperature_offset_word(milli_celsius)
2921}
2922
2923#[no_mangle]
2929pub extern "C" fn pamoja_scd4x_temperature_offset_milli_celsius(word: u16) -> u32 {
2930 scd4x::temperature_offset_milli_celsius(word)
2931}
2932
2933#[no_mangle]
2939pub extern "C" fn pamoja_scd4x_ambient_pressure_word(pascals: u32) -> u16 {
2940 scd4x::ambient_pressure_word(pascals)
2941}
2942
2943#[no_mangle]
2949pub extern "C" fn pamoja_scd4x_ambient_pressure_pascals(word: u16) -> u32 {
2950 scd4x::ambient_pressure_pascals(word)
2951}
2952
2953#[no_mangle]
2964pub unsafe extern "C" fn pamoja_scd4x_forced_recalibration_correction_ppm(
2965 word: u16,
2966 out_ppm: *mut i32,
2967) -> bool {
2968 match scd4x::forced_recalibration_correction_ppm(word) {
2969 Some(ppm) if !out_ppm.is_null() => {
2970 *out_ppm = ppm;
2971 true
2972 }
2973 _ => false,
2974 }
2975}
2976
2977#[no_mangle]
2983pub extern "C" fn pamoja_scd4x_forced_recalibration_word(
2984 succeeded: bool,
2985 correction_ppm: i32,
2986) -> u16 {
2987 scd4x::forced_recalibration_word(succeeded.then_some(correction_ppm))
2988}
2989
2990#[no_mangle]
2996pub extern "C" fn pamoja_scd4x_automatic_self_calibration_enabled(word: u16) -> bool {
2997 scd4x::automatic_self_calibration_enabled(word)
2998}
2999
3000#[no_mangle]
3006pub extern "C" fn pamoja_scd4x_automatic_self_calibration_word(enabled: bool) -> u16 {
3007 scd4x::automatic_self_calibration_word(enabled)
3008}
3009
3010#[no_mangle]
3016pub extern "C" fn pamoja_scd4x_self_test_passed(word: u16) -> bool {
3017 scd4x::self_test_passed(word)
3018}
3019
3020#[no_mangle]
3032pub unsafe extern "C" fn pamoja_scd4x_serial_number(
3033 frame: *const u8,
3034 frame_len: usize,
3035 out_serial: *mut u64,
3036) -> PamojaStatus {
3037 if out_serial.is_null() {
3038 set_last_error("out_serial must not be null".to_owned());
3039 return PamojaStatus::InvalidArgument;
3040 }
3041 let frame = match read_bytes(frame, frame_len) {
3042 Ok(bytes) => bytes,
3043 Err(status) => return status,
3044 };
3045 let Ok(frame) = <[u8; PAMOJA_SCD4X_MEASUREMENT_LEN]>::try_from(&frame[..]) else {
3046 return wrong_length("serial number frame", PAMOJA_SCD4X_MEASUREMENT_LEN);
3047 };
3048 match scd4x::serial_number(&frame) {
3049 Ok(serial) => {
3050 *out_serial = serial;
3051 PamojaStatus::Ok
3052 }
3053 Err(error) => failed(error),
3054 }
3055}
3056
3057#[no_mangle]
3068pub unsafe extern "C" fn pamoja_scd4x_serial_number_frame(
3069 serial: u64,
3070 out_bytes: *mut u8,
3071) -> PamojaStatus {
3072 if out_bytes.is_null() {
3073 set_last_error("out_bytes must not be null".to_owned());
3074 return PamojaStatus::InvalidArgument;
3075 }
3076 let bytes = scd4x::serial_number_frame(serial);
3077 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out_bytes, PAMOJA_SCD4X_MEASUREMENT_LEN);
3078 PamojaStatus::Ok
3079}
3080
3081pub const PAMOJA_TMP117_REGISTER_LEN: usize = 2;
3083
3084pub const PAMOJA_TMP117_DEVICE_ID: u16 = 0x0117;
3086
3087pub const PAMOJA_TMP117_CONFIG_RESET: u16 = 0x0220;
3089
3090pub const PAMOJA_TMP117_HIGH_LIMIT_RESET: u16 = 0x6000;
3092
3093pub const PAMOJA_TMP117_LOW_LIMIT_RESET: u16 = 0x8000;
3095
3096pub const PAMOJA_TMP117_TEMP_RESULT_RESET: u16 = 0x8000;
3098
3099pub const PAMOJA_TMP117_GENERAL_CALL_RESET: u8 = 0x06;
3101
3102pub const PAMOJA_TMP117_EEPROM_UNLOCK: u16 = 0x8000;
3104
3105pub const PAMOJA_TMP117_ADDRESS_ADD0_GND: u8 = 0x48;
3107
3108pub const PAMOJA_TMP117_ADDRESS_ADD0_VPLUS: u8 = 0x49;
3110
3111pub const PAMOJA_TMP117_ADDRESS_ADD0_SDA: u8 = 0x4A;
3113
3114pub const PAMOJA_TMP117_ADDRESS_ADD0_SCL: u8 = 0x4B;
3116
3117pub const PAMOJA_TMP117_REGISTER_TEMP_RESULT: u8 = 0x00;
3119
3120pub const PAMOJA_TMP117_REGISTER_CONFIGURATION: u8 = 0x01;
3122
3123pub const PAMOJA_TMP117_REGISTER_THIGH_LIMIT: u8 = 0x02;
3125
3126pub const PAMOJA_TMP117_REGISTER_TLOW_LIMIT: u8 = 0x03;
3128
3129pub const PAMOJA_TMP117_REGISTER_EEPROM_UL: u8 = 0x04;
3131
3132pub const PAMOJA_TMP117_REGISTER_EEPROM1: u8 = 0x05;
3134
3135pub const PAMOJA_TMP117_REGISTER_EEPROM2: u8 = 0x06;
3137
3138pub const PAMOJA_TMP117_REGISTER_TEMP_OFFSET: u8 = 0x07;
3140
3141pub const PAMOJA_TMP117_REGISTER_EEPROM3: u8 = 0x08;
3143
3144pub const PAMOJA_TMP117_REGISTER_DEVICE_ID: u8 = 0x0F;
3146
3147const _: () = assert!(PAMOJA_TMP117_DEVICE_ID == tmp117::DEVICE_ID);
3150const _: () = assert!(PAMOJA_TMP117_CONFIG_RESET == tmp117::CONFIG_RESET);
3151const _: () = assert!(PAMOJA_TMP117_HIGH_LIMIT_RESET == tmp117::HIGH_LIMIT_RESET);
3152const _: () = assert!(PAMOJA_TMP117_LOW_LIMIT_RESET == tmp117::LOW_LIMIT_RESET);
3153const _: () = assert!(PAMOJA_TMP117_TEMP_RESULT_RESET == tmp117::TEMP_RESULT_RESET);
3154const _: () = assert!(PAMOJA_TMP117_GENERAL_CALL_RESET == tmp117::GENERAL_CALL_RESET);
3155const _: () = assert!(PAMOJA_TMP117_EEPROM_UNLOCK == tmp117::EEPROM_UNLOCK);
3156const _: () = assert!(PAMOJA_TMP117_ADDRESS_ADD0_GND == tmp117::address::ADD0_GND);
3157const _: () = assert!(PAMOJA_TMP117_ADDRESS_ADD0_VPLUS == tmp117::address::ADD0_VPLUS);
3158const _: () = assert!(PAMOJA_TMP117_ADDRESS_ADD0_SDA == tmp117::address::ADD0_SDA);
3159const _: () = assert!(PAMOJA_TMP117_ADDRESS_ADD0_SCL == tmp117::address::ADD0_SCL);
3160const _: () = assert!(PAMOJA_TMP117_REGISTER_TEMP_RESULT == tmp117::register::TEMP_RESULT);
3161const _: () = assert!(PAMOJA_TMP117_REGISTER_CONFIGURATION == tmp117::register::CONFIGURATION);
3162const _: () = assert!(PAMOJA_TMP117_REGISTER_THIGH_LIMIT == tmp117::register::THIGH_LIMIT);
3163const _: () = assert!(PAMOJA_TMP117_REGISTER_TLOW_LIMIT == tmp117::register::TLOW_LIMIT);
3164const _: () = assert!(PAMOJA_TMP117_REGISTER_EEPROM_UL == tmp117::register::EEPROM_UL);
3165const _: () = assert!(PAMOJA_TMP117_REGISTER_EEPROM1 == tmp117::register::EEPROM1);
3166const _: () = assert!(PAMOJA_TMP117_REGISTER_EEPROM2 == tmp117::register::EEPROM2);
3167const _: () = assert!(PAMOJA_TMP117_REGISTER_TEMP_OFFSET == tmp117::register::TEMP_OFFSET);
3168const _: () = assert!(PAMOJA_TMP117_REGISTER_EEPROM3 == tmp117::register::EEPROM3);
3169const _: () = assert!(PAMOJA_TMP117_REGISTER_DEVICE_ID == tmp117::register::DEVICE_ID);
3170
3171#[repr(C)]
3173#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3174pub struct PamojaTmp117Config {
3175 pub high_alert: u8,
3177 pub low_alert: u8,
3179 pub data_ready: u8,
3181 pub eeprom_busy: u8,
3183 pub mode: u8,
3185 pub cycle: u8,
3187 pub averaging: u8,
3189 pub therm_mode: u8,
3191 pub alert_active_high: u8,
3193 pub alert_pin_data_ready: u8,
3195 pub soft_reset: u8,
3197}
3198
3199#[no_mangle]
3205pub extern "C" fn pamoja_tmp117_nano_celsius(raw: i16) -> i64 {
3206 tmp117::nano_celsius(raw)
3207}
3208
3209#[no_mangle]
3215pub extern "C" fn pamoja_tmp117_micro_celsius(raw: i16) -> i32 {
3216 tmp117::micro_celsius(raw)
3217}
3218
3219#[no_mangle]
3225pub extern "C" fn pamoja_tmp117_celsius(raw: i16) -> f32 {
3226 tmp117::celsius(raw)
3227}
3228
3229#[no_mangle]
3235pub extern "C" fn pamoja_tmp117_raw_from_micro_celsius(micro_celsius: i32) -> i16 {
3236 tmp117::raw_from_micro_celsius(micro_celsius)
3237}
3238
3239#[no_mangle]
3245pub extern "C" fn pamoja_tmp117_raw_from_celsius(celsius: f32) -> i16 {
3246 tmp117::raw_from_celsius(celsius)
3247}
3248
3249#[no_mangle]
3260pub unsafe extern "C" fn pamoja_tmp117_temperature_bytes(
3261 raw: i16,
3262 out_bytes: *mut u8,
3263) -> PamojaStatus {
3264 if out_bytes.is_null() {
3265 set_last_error("out_bytes must not be null".to_owned());
3266 return PamojaStatus::InvalidArgument;
3267 }
3268 let bytes = tmp117::temperature_bytes(raw);
3269 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out_bytes, PAMOJA_TMP117_REGISTER_LEN);
3270 PamojaStatus::Ok
3271}
3272
3273#[no_mangle]
3285pub unsafe extern "C" fn pamoja_tmp117_temperature_from_bytes(
3286 bytes: *const u8,
3287 bytes_len: usize,
3288 out_raw: *mut i16,
3289) -> PamojaStatus {
3290 if out_raw.is_null() {
3291 set_last_error("out_raw must not be null".to_owned());
3292 return PamojaStatus::InvalidArgument;
3293 }
3294 let bytes = match read_bytes(bytes, bytes_len) {
3295 Ok(bytes) => bytes,
3296 Err(status) => return status,
3297 };
3298 let Ok(bytes) = <[u8; PAMOJA_TMP117_REGISTER_LEN]>::try_from(&bytes[..]) else {
3299 return wrong_length("temperature register", PAMOJA_TMP117_REGISTER_LEN);
3300 };
3301 *out_raw = tmp117::temperature_from_bytes(bytes);
3302 PamojaStatus::Ok
3303}
3304
3305#[no_mangle]
3311pub extern "C" fn pamoja_tmp117_device_id(raw: u16) -> u16 {
3312 tmp117::device_id(raw)
3313}
3314
3315#[no_mangle]
3321pub extern "C" fn pamoja_tmp117_revision(raw: u16) -> u8 {
3322 tmp117::revision(raw)
3323}
3324
3325#[no_mangle]
3331pub extern "C" fn pamoja_tmp117_high_alert(config: u16) -> bool {
3332 tmp117::high_alert(config)
3333}
3334
3335#[no_mangle]
3341pub extern "C" fn pamoja_tmp117_low_alert(config: u16) -> bool {
3342 tmp117::low_alert(config)
3343}
3344
3345#[no_mangle]
3351pub extern "C" fn pamoja_tmp117_data_ready(config: u16) -> bool {
3352 tmp117::data_ready(config)
3353}
3354
3355#[no_mangle]
3361pub extern "C" fn pamoja_tmp117_eeprom_busy(config: u16) -> bool {
3362 tmp117::eeprom_busy(config)
3363}
3364
3365#[no_mangle]
3371pub extern "C" fn pamoja_tmp117_eeprom_unlock_busy(unlock: u16) -> bool {
3372 tmp117::eeprom_unlock_busy(unlock)
3373}
3374
3375#[no_mangle]
3381pub extern "C" fn pamoja_tmp117_config_bits(config: PamojaTmp117Config) -> u16 {
3382 tmp117::Configuration::from(config).bits()
3383}
3384
3385#[no_mangle]
3396pub unsafe extern "C" fn pamoja_tmp117_config_from_bits(
3397 bits: u16,
3398 out_config: *mut PamojaTmp117Config,
3399) -> PamojaStatus {
3400 if out_config.is_null() {
3401 set_last_error("out_config must not be null".to_owned());
3402 return PamojaStatus::InvalidArgument;
3403 }
3404 *out_config = tmp117::Configuration::from_bits(bits).into();
3405 PamojaStatus::Ok
3406}
3407
3408#[no_mangle]
3414pub extern "C" fn pamoja_tmp117_averaging_conversions(code: u8) -> u8 {
3415 tmp117::Averaging::from_code(code).conversions()
3416}
3417
3418#[no_mangle]
3424pub extern "C" fn pamoja_tmp117_averaging_micros(code: u8) -> u32 {
3425 tmp117::Averaging::from_code(code).conversion_micros()
3426}
3427
3428#[no_mangle]
3434pub extern "C" fn pamoja_tmp117_cycle_nominal_micros(code: u8) -> u32 {
3435 tmp117::ConversionCycle::from_code(code).nominal_micros()
3436}
3437
3438#[no_mangle]
3444pub extern "C" fn pamoja_tmp117_cycle_micros(cycle: u8, averaging: u8) -> u32 {
3445 let averaging = tmp117::Averaging::from_code(averaging);
3446 tmp117::ConversionCycle::from_code(cycle).cycle_micros(averaging)
3447}
3448
3449pub const PAMOJA_HDC1080_MEASUREMENT_LEN: usize = 4;
3451
3452pub const PAMOJA_HDC1080_SERIAL_ID_REGISTERS: usize = 3;
3454
3455pub const PAMOJA_HDC1080_I2C_ADDRESS: u8 = 0x40;
3457
3458pub const PAMOJA_HDC1080_MANUFACTURER_ID: u16 = 0x5449;
3460
3461pub const PAMOJA_HDC1080_DEVICE_ID: u16 = 0x1050;
3463
3464pub const PAMOJA_HDC1080_CONFIGURATION_RESET: u16 = 0x1000;
3466
3467pub const PAMOJA_HDC1080_REGISTER_TEMPERATURE: u8 = 0x00;
3469
3470pub const PAMOJA_HDC1080_REGISTER_HUMIDITY: u8 = 0x01;
3472
3473pub const PAMOJA_HDC1080_REGISTER_CONFIGURATION: u8 = 0x02;
3475
3476pub const PAMOJA_HDC1080_REGISTER_SERIAL_ID_HIGH: u8 = 0xFB;
3478
3479pub const PAMOJA_HDC1080_REGISTER_SERIAL_ID_MID: u8 = 0xFC;
3481
3482pub const PAMOJA_HDC1080_REGISTER_SERIAL_ID_LOW: u8 = 0xFD;
3484
3485pub const PAMOJA_HDC1080_REGISTER_MANUFACTURER_ID: u8 = 0xFE;
3487
3488pub const PAMOJA_HDC1080_REGISTER_DEVICE_ID: u8 = 0xFF;
3490
3491const _: () = assert!(PAMOJA_HDC1080_I2C_ADDRESS == hdc1080::I2C_ADDRESS);
3494const _: () = assert!(PAMOJA_HDC1080_MANUFACTURER_ID == hdc1080::MANUFACTURER_ID);
3495const _: () = assert!(PAMOJA_HDC1080_DEVICE_ID == hdc1080::DEVICE_ID);
3496const _: () = assert!(PAMOJA_HDC1080_CONFIGURATION_RESET == hdc1080::CONFIGURATION_RESET);
3497const _: () = assert!(PAMOJA_HDC1080_REGISTER_TEMPERATURE == hdc1080::register::TEMPERATURE);
3498const _: () = assert!(PAMOJA_HDC1080_REGISTER_HUMIDITY == hdc1080::register::HUMIDITY);
3499const _: () = assert!(PAMOJA_HDC1080_REGISTER_CONFIGURATION == hdc1080::register::CONFIGURATION);
3500const _: () = assert!(PAMOJA_HDC1080_REGISTER_SERIAL_ID_HIGH == hdc1080::register::SERIAL_ID_HIGH);
3501const _: () = assert!(PAMOJA_HDC1080_REGISTER_SERIAL_ID_MID == hdc1080::register::SERIAL_ID_MID);
3502const _: () = assert!(PAMOJA_HDC1080_REGISTER_SERIAL_ID_LOW == hdc1080::register::SERIAL_ID_LOW);
3503const _: () =
3504 assert!(PAMOJA_HDC1080_REGISTER_MANUFACTURER_ID == hdc1080::register::MANUFACTURER_ID);
3505const _: () = assert!(PAMOJA_HDC1080_REGISTER_DEVICE_ID == hdc1080::register::DEVICE_ID);
3506
3507#[repr(C)]
3509#[derive(Clone, Copy, Debug, PartialEq)]
3510pub struct PamojaHdc1080Measurement {
3511 pub temperature_raw: u16,
3513 pub humidity_raw: u16,
3515 pub milli_celsius: i32,
3517 pub celsius: f32,
3519 pub milli_percent: u32,
3521 pub relative_humidity: f32,
3523}
3524
3525#[repr(C)]
3527#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3528pub struct PamojaHdc1080Config {
3529 pub software_reset: u8,
3531 pub heater: u8,
3533 pub sequential: u8,
3535 pub battery_low: u8,
3537 pub temperature_resolution_bits: u8,
3539 pub humidity_resolution_bits: u8,
3541}
3542
3543#[no_mangle]
3549pub extern "C" fn pamoja_hdc1080_milli_celsius(raw: u16) -> i32 {
3550 hdc1080::milli_celsius(raw)
3551}
3552
3553#[no_mangle]
3559pub extern "C" fn pamoja_hdc1080_celsius(raw: u16) -> f32 {
3560 hdc1080::celsius(raw)
3561}
3562
3563#[no_mangle]
3569pub extern "C" fn pamoja_hdc1080_milli_percent(raw: u16) -> u32 {
3570 hdc1080::milli_percent(raw)
3571}
3572
3573#[no_mangle]
3579pub extern "C" fn pamoja_hdc1080_relative_humidity(raw: u16) -> f32 {
3580 hdc1080::relative_humidity(raw)
3581}
3582
3583#[no_mangle]
3589pub extern "C" fn pamoja_hdc1080_temperature_register(milli_celsius: i32) -> u16 {
3590 hdc1080::temperature_register(milli_celsius)
3591}
3592
3593#[no_mangle]
3599pub extern "C" fn pamoja_hdc1080_humidity_register(milli_percent: u32) -> u16 {
3600 hdc1080::humidity_register(milli_percent)
3601}
3602
3603#[no_mangle]
3609pub extern "C" fn pamoja_hdc1080_serial_id(high: u16, mid: u16, low: u16) -> u64 {
3610 hdc1080::serial_id(high, mid, low)
3611}
3612
3613#[no_mangle]
3625pub unsafe extern "C" fn pamoja_hdc1080_serial_id_registers(
3626 serial: u64,
3627 out_registers: *mut u16,
3628) -> PamojaStatus {
3629 if out_registers.is_null() {
3630 set_last_error("out_registers must not be null".to_owned());
3631 return PamojaStatus::InvalidArgument;
3632 }
3633 let registers = hdc1080::serial_id_registers(serial);
3634 core::ptr::copy_nonoverlapping(
3635 registers.as_ptr(),
3636 out_registers,
3637 PAMOJA_HDC1080_SERIAL_ID_REGISTERS,
3638 );
3639 PamojaStatus::Ok
3640}
3641
3642#[no_mangle]
3655pub unsafe extern "C" fn pamoja_hdc1080_parse_measurement(
3656 bytes: *const u8,
3657 bytes_len: usize,
3658 out_measurement: *mut PamojaHdc1080Measurement,
3659) -> PamojaStatus {
3660 if out_measurement.is_null() {
3661 set_last_error("out_measurement must not be null".to_owned());
3662 return PamojaStatus::InvalidArgument;
3663 }
3664 let bytes = match read_bytes(bytes, bytes_len) {
3665 Ok(bytes) => bytes,
3666 Err(status) => return status,
3667 };
3668 let Ok(bytes) = <[u8; PAMOJA_HDC1080_MEASUREMENT_LEN]>::try_from(&bytes[..]) else {
3669 return wrong_length("measurement", PAMOJA_HDC1080_MEASUREMENT_LEN);
3670 };
3671 *out_measurement = hdc1080::Measurement::parse(&bytes).into();
3672 PamojaStatus::Ok
3673}
3674
3675#[no_mangle]
3686pub unsafe extern "C" fn pamoja_hdc1080_measurement_from_physical(
3687 milli_celsius: i32,
3688 milli_percent: u32,
3689 out_measurement: *mut PamojaHdc1080Measurement,
3690) -> PamojaStatus {
3691 if out_measurement.is_null() {
3692 set_last_error("out_measurement must not be null".to_owned());
3693 return PamojaStatus::InvalidArgument;
3694 }
3695 let measurement = hdc1080::Measurement::from_physical(milli_celsius, milli_percent);
3696 *out_measurement = measurement.into();
3697 PamojaStatus::Ok
3698}
3699
3700#[no_mangle]
3711pub unsafe extern "C" fn pamoja_hdc1080_measurement_bytes(
3712 temperature_raw: u16,
3713 humidity_raw: u16,
3714 out_bytes: *mut u8,
3715) -> PamojaStatus {
3716 if out_bytes.is_null() {
3717 set_last_error("out_bytes must not be null".to_owned());
3718 return PamojaStatus::InvalidArgument;
3719 }
3720 let measurement = hdc1080::Measurement {
3721 temperature: temperature_raw,
3722 humidity: humidity_raw,
3723 };
3724 let bytes = measurement.to_bytes();
3725 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out_bytes, PAMOJA_HDC1080_MEASUREMENT_LEN);
3726 PamojaStatus::Ok
3727}
3728
3729#[no_mangle]
3742pub unsafe extern "C" fn pamoja_hdc1080_config_from_register(
3743 raw: u16,
3744 out_config: *mut PamojaHdc1080Config,
3745) -> PamojaStatus {
3746 if out_config.is_null() {
3747 set_last_error("out_config must not be null".to_owned());
3748 return PamojaStatus::InvalidArgument;
3749 }
3750 match hdc1080::Configuration::from_register(raw) {
3751 Ok(config) => {
3752 *out_config = config.into();
3753 PamojaStatus::Ok
3754 }
3755 Err(error) => failed(error),
3756 }
3757}
3758
3759#[no_mangle]
3771pub unsafe extern "C" fn pamoja_hdc1080_config_to_register(
3772 config: PamojaHdc1080Config,
3773 out_register: *mut u16,
3774) -> PamojaStatus {
3775 if out_register.is_null() {
3776 set_last_error("out_register must not be null".to_owned());
3777 return PamojaStatus::InvalidArgument;
3778 }
3779 match hdc1080::Configuration::try_from(config) {
3780 Ok(config) => {
3781 *out_register = config.to_register();
3782 PamojaStatus::Ok
3783 }
3784 Err(status) => status,
3785 }
3786}
3787
3788#[no_mangle]
3800pub unsafe extern "C" fn pamoja_hdc1080_conversion_time_micros(
3801 config: PamojaHdc1080Config,
3802 out_micros: *mut u32,
3803) -> PamojaStatus {
3804 if out_micros.is_null() {
3805 set_last_error("out_micros must not be null".to_owned());
3806 return PamojaStatus::InvalidArgument;
3807 }
3808 match hdc1080::Configuration::try_from(config) {
3809 Ok(config) => {
3810 *out_micros = config.conversion_time_micros();
3811 PamojaStatus::Ok
3812 }
3813 Err(status) => status,
3814 }
3815}
3816
3817#[no_mangle]
3828pub unsafe extern "C" fn pamoja_hdc1080_temperature_conversion_micros(
3829 bits: u8,
3830 out_micros: *mut u32,
3831) -> PamojaStatus {
3832 if out_micros.is_null() {
3833 set_last_error("out_micros must not be null".to_owned());
3834 return PamojaStatus::InvalidArgument;
3835 }
3836 match temperature_resolution(bits) {
3837 Some(resolution) => {
3838 *out_micros = resolution.conversion_time_micros();
3839 PamojaStatus::Ok
3840 }
3841 None => bad_temperature_resolution(),
3842 }
3843}
3844
3845#[no_mangle]
3856pub unsafe extern "C" fn pamoja_hdc1080_humidity_conversion_micros(
3857 bits: u8,
3858 out_micros: *mut u32,
3859) -> PamojaStatus {
3860 if out_micros.is_null() {
3861 set_last_error("out_micros must not be null".to_owned());
3862 return PamojaStatus::InvalidArgument;
3863 }
3864 match humidity_resolution(bits) {
3865 Some(resolution) => {
3866 *out_micros = resolution.conversion_time_micros();
3867 PamojaStatus::Ok
3868 }
3869 None => bad_humidity_resolution(),
3870 }
3871}
3872
3873pub const PAMOJA_OPT3001_REGISTER_LEN: usize = 2;
3875
3876pub const PAMOJA_OPT3001_I2C_ADDRESS_GND: u8 = 0x44;
3878
3879pub const PAMOJA_OPT3001_I2C_ADDRESS_VDD: u8 = 0x45;
3881
3882pub const PAMOJA_OPT3001_I2C_ADDRESS_SDA: u8 = 0x46;
3884
3885pub const PAMOJA_OPT3001_I2C_ADDRESS_SCL: u8 = 0x47;
3887
3888pub const PAMOJA_OPT3001_MANUFACTURER_ID: u16 = 0x5449;
3890
3891pub const PAMOJA_OPT3001_DEVICE_ID: u16 = 0x3001;
3893
3894pub const PAMOJA_OPT3001_CONFIGURATION_RESET: u16 = 0xC810;
3896
3897pub const PAMOJA_OPT3001_LOW_LIMIT_RESET: u16 = 0x0000;
3899
3900pub const PAMOJA_OPT3001_HIGH_LIMIT_RESET: u16 = 0xBFFF;
3902
3903pub const PAMOJA_OPT3001_LOW_LIMIT_END_OF_CONVERSION: u16 = 0xC000;
3905
3906pub const PAMOJA_OPT3001_RANGE_AUTOMATIC: u8 = 0b1100;
3908
3909pub const PAMOJA_OPT3001_RANGE_MAX: u8 = 11;
3911
3912pub const PAMOJA_OPT3001_REGISTER_RESULT: u8 = 0x00;
3914
3915pub const PAMOJA_OPT3001_REGISTER_CONFIGURATION: u8 = 0x01;
3917
3918pub const PAMOJA_OPT3001_REGISTER_LOW_LIMIT: u8 = 0x02;
3920
3921pub const PAMOJA_OPT3001_REGISTER_HIGH_LIMIT: u8 = 0x03;
3923
3924pub const PAMOJA_OPT3001_REGISTER_MANUFACTURER_ID: u8 = 0x7E;
3926
3927pub const PAMOJA_OPT3001_REGISTER_DEVICE_ID: u8 = 0x7F;
3929
3930const _: () = assert!(PAMOJA_OPT3001_I2C_ADDRESS_GND == opt3001::I2C_ADDRESS_GND);
3933const _: () = assert!(PAMOJA_OPT3001_I2C_ADDRESS_VDD == opt3001::I2C_ADDRESS_VDD);
3934const _: () = assert!(PAMOJA_OPT3001_I2C_ADDRESS_SDA == opt3001::I2C_ADDRESS_SDA);
3935const _: () = assert!(PAMOJA_OPT3001_I2C_ADDRESS_SCL == opt3001::I2C_ADDRESS_SCL);
3936const _: () = assert!(PAMOJA_OPT3001_MANUFACTURER_ID == opt3001::MANUFACTURER_ID);
3937const _: () = assert!(PAMOJA_OPT3001_DEVICE_ID == opt3001::DEVICE_ID);
3938const _: () = assert!(PAMOJA_OPT3001_CONFIGURATION_RESET == opt3001::CONFIGURATION_RESET);
3939const _: () = assert!(PAMOJA_OPT3001_LOW_LIMIT_RESET == opt3001::LOW_LIMIT_RESET);
3940const _: () = assert!(PAMOJA_OPT3001_HIGH_LIMIT_RESET == opt3001::HIGH_LIMIT_RESET);
3941const _: () =
3942 assert!(PAMOJA_OPT3001_LOW_LIMIT_END_OF_CONVERSION == opt3001::LOW_LIMIT_END_OF_CONVERSION);
3943const _: () = assert!(PAMOJA_OPT3001_RANGE_AUTOMATIC == opt3001::RANGE_AUTOMATIC);
3944const _: () = assert!(PAMOJA_OPT3001_RANGE_MAX == opt3001::RANGE_MAX);
3945const _: () = assert!(PAMOJA_OPT3001_REGISTER_RESULT == opt3001::register::RESULT);
3946const _: () = assert!(PAMOJA_OPT3001_REGISTER_CONFIGURATION == opt3001::register::CONFIGURATION);
3947const _: () = assert!(PAMOJA_OPT3001_REGISTER_LOW_LIMIT == opt3001::register::LOW_LIMIT);
3948const _: () = assert!(PAMOJA_OPT3001_REGISTER_HIGH_LIMIT == opt3001::register::HIGH_LIMIT);
3949const _: () =
3950 assert!(PAMOJA_OPT3001_REGISTER_MANUFACTURER_ID == opt3001::register::MANUFACTURER_ID);
3951const _: () = assert!(PAMOJA_OPT3001_REGISTER_DEVICE_ID == opt3001::register::DEVICE_ID);
3952
3953#[repr(C)]
3955#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3956pub struct PamojaOpt3001Config {
3957 pub range_number: u8,
3959 pub long_conversion: u8,
3961 pub mode: u8,
3963 pub overflow: u8,
3965 pub conversion_ready: u8,
3967 pub flag_high: u8,
3969 pub flag_low: u8,
3971 pub latched_window: u8,
3973 pub active_high: u8,
3975 pub mask_exponent: u8,
3977 pub fault_count: u8,
3979}
3980
3981#[no_mangle]
3992pub unsafe extern "C" fn pamoja_opt3001_lsb_milli_lux(
3993 exponent: u8,
3994 out_milli_lux: *mut u32,
3995) -> bool {
3996 match opt3001::lsb_milli_lux(exponent) {
3997 Some(step) if !out_milli_lux.is_null() => {
3998 *out_milli_lux = step;
3999 true
4000 }
4001 _ => false,
4002 }
4003}
4004
4005#[no_mangle]
4016pub unsafe extern "C" fn pamoja_opt3001_full_scale_milli_lux(
4017 range_number: u8,
4018 out_milli_lux: *mut u32,
4019) -> bool {
4020 match opt3001::full_scale_milli_lux(range_number) {
4021 Some(full_scale) if !out_milli_lux.is_null() => {
4022 *out_milli_lux = full_scale;
4023 true
4024 }
4025 _ => false,
4026 }
4027}
4028
4029#[no_mangle]
4035pub extern "C" fn pamoja_opt3001_milli_lux(raw: u16) -> u32 {
4036 opt3001::milli_lux(raw)
4037}
4038
4039#[no_mangle]
4045pub extern "C" fn pamoja_opt3001_lux(raw: u16) -> f32 {
4046 opt3001::lux(raw)
4047}
4048
4049#[no_mangle]
4056pub extern "C" fn pamoja_opt3001_raw_from_milli_lux(milli_lux: u32) -> u16 {
4057 opt3001::raw_from_milli_lux(milli_lux)
4058}
4059
4060#[no_mangle]
4072pub unsafe extern "C" fn pamoja_opt3001_word_from_bytes(
4073 bytes: *const u8,
4074 bytes_len: usize,
4075 out_word: *mut u16,
4076) -> PamojaStatus {
4077 if out_word.is_null() {
4078 set_last_error("out_word must not be null".to_owned());
4079 return PamojaStatus::InvalidArgument;
4080 }
4081 let bytes = match read_bytes(bytes, bytes_len) {
4082 Ok(bytes) => bytes,
4083 Err(status) => return status,
4084 };
4085 let Ok(bytes) = <[u8; PAMOJA_OPT3001_REGISTER_LEN]>::try_from(&bytes[..]) else {
4086 return wrong_length("register", PAMOJA_OPT3001_REGISTER_LEN);
4087 };
4088 *out_word = opt3001::word_from_bytes(bytes);
4089 PamojaStatus::Ok
4090}
4091
4092#[no_mangle]
4103pub unsafe extern "C" fn pamoja_opt3001_word_to_bytes(
4104 word: u16,
4105 out_bytes: *mut u8,
4106) -> PamojaStatus {
4107 if out_bytes.is_null() {
4108 set_last_error("out_bytes must not be null".to_owned());
4109 return PamojaStatus::InvalidArgument;
4110 }
4111 let bytes = opt3001::word_to_bytes(word);
4112 core::ptr::copy_nonoverlapping(bytes.as_ptr(), out_bytes, PAMOJA_OPT3001_REGISTER_LEN);
4113 PamojaStatus::Ok
4114}
4115
4116#[no_mangle]
4122pub extern "C" fn pamoja_opt3001_config_bits(config: PamojaOpt3001Config) -> u16 {
4123 opt3001::Configuration::from(config).bits()
4124}
4125
4126#[no_mangle]
4137pub unsafe extern "C" fn pamoja_opt3001_config_from_bits(
4138 bits: u16,
4139 out_config: *mut PamojaOpt3001Config,
4140) -> PamojaStatus {
4141 if out_config.is_null() {
4142 set_last_error("out_config must not be null".to_owned());
4143 return PamojaStatus::InvalidArgument;
4144 }
4145 *out_config = opt3001::Configuration::from_bits(bits).into();
4146 PamojaStatus::Ok
4147}
4148
4149#[no_mangle]
4155pub extern "C" fn pamoja_opt3001_conversion_millis(long_conversion: bool) -> u16 {
4156 conversion_time(long_conversion).millis()
4157}
4158
4159#[no_mangle]
4165pub extern "C" fn pamoja_opt3001_fault_count(code: u8) -> u8 {
4166 opt3001::FaultCount::from_code(code).count()
4167}
4168
4169#[no_mangle]
4175pub extern "C" fn pamoja_opt3001_is_automatic_range(range_number: u8) -> bool {
4176 range_number == opt3001::RANGE_AUTOMATIC
4177}
4178
4179pub const PAMOJA_INA226_BASE_ADDRESS: u8 = 0x40;
4181
4182pub const PAMOJA_INA226_MANUFACTURER_ID: u16 = 0x5449;
4184
4185pub const PAMOJA_INA226_DEVICE_ID: u16 = 0x226;
4187
4188pub const PAMOJA_INA226_CONFIG_RESET: u16 = 0x4127;
4190
4191pub const PAMOJA_INA226_SHUNT_LSB_NANOVOLTS: i32 = 2_500;
4193
4194pub const PAMOJA_INA226_BUS_LSB_MICROVOLTS: u32 = 1_250;
4196
4197pub const PAMOJA_INA226_POWER_LSB_RATIO: u32 = 25;
4199
4200pub const PAMOJA_INA226_REGISTER_CONFIGURATION: u8 = 0x00;
4202
4203pub const PAMOJA_INA226_REGISTER_SHUNT_VOLTAGE: u8 = 0x01;
4205
4206pub const PAMOJA_INA226_REGISTER_BUS_VOLTAGE: u8 = 0x02;
4208
4209pub const PAMOJA_INA226_REGISTER_POWER: u8 = 0x03;
4211
4212pub const PAMOJA_INA226_REGISTER_CURRENT: u8 = 0x04;
4214
4215pub const PAMOJA_INA226_REGISTER_CALIBRATION: u8 = 0x05;
4217
4218pub const PAMOJA_INA226_REGISTER_MASK_ENABLE: u8 = 0x06;
4220
4221pub const PAMOJA_INA226_REGISTER_ALERT_LIMIT: u8 = 0x07;
4223
4224pub const PAMOJA_INA226_REGISTER_MANUFACTURER_ID: u8 = 0xFE;
4226
4227pub const PAMOJA_INA226_REGISTER_DIE_ID: u8 = 0xFF;
4229
4230const _: () = assert!(PAMOJA_INA226_BASE_ADDRESS == ina226::BASE_ADDRESS);
4233const _: () = assert!(PAMOJA_INA226_MANUFACTURER_ID == ina226::MANUFACTURER_ID);
4234const _: () = assert!(PAMOJA_INA226_DEVICE_ID == ina226::DEVICE_ID);
4235const _: () = assert!(PAMOJA_INA226_CONFIG_RESET == ina226::CONFIG_RESET);
4236const _: () = assert!(PAMOJA_INA226_SHUNT_LSB_NANOVOLTS == ina226::SHUNT_LSB_NANOVOLTS);
4237const _: () = assert!(PAMOJA_INA226_BUS_LSB_MICROVOLTS == ina226::BUS_LSB_MICROVOLTS);
4238const _: () = assert!(PAMOJA_INA226_POWER_LSB_RATIO == ina226::POWER_LSB_RATIO);
4239const _: () = assert!(PAMOJA_INA226_REGISTER_CONFIGURATION == ina226::register::CONFIGURATION);
4240const _: () = assert!(PAMOJA_INA226_REGISTER_SHUNT_VOLTAGE == ina226::register::SHUNT_VOLTAGE);
4241const _: () = assert!(PAMOJA_INA226_REGISTER_BUS_VOLTAGE == ina226::register::BUS_VOLTAGE);
4242const _: () = assert!(PAMOJA_INA226_REGISTER_POWER == ina226::register::POWER);
4243const _: () = assert!(PAMOJA_INA226_REGISTER_CURRENT == ina226::register::CURRENT);
4244const _: () = assert!(PAMOJA_INA226_REGISTER_CALIBRATION == ina226::register::CALIBRATION);
4245const _: () = assert!(PAMOJA_INA226_REGISTER_MASK_ENABLE == ina226::register::MASK_ENABLE);
4246const _: () = assert!(PAMOJA_INA226_REGISTER_ALERT_LIMIT == ina226::register::ALERT_LIMIT);
4247const _: () = assert!(PAMOJA_INA226_REGISTER_MANUFACTURER_ID == ina226::register::MANUFACTURER_ID);
4248const _: () = assert!(PAMOJA_INA226_REGISTER_DIE_ID == ina226::register::DIE_ID);
4249
4250#[repr(C)]
4252#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4253pub enum PamojaIna226AlertFunction {
4254 ShuntOverLimit = 0,
4256 ShuntUnderLimit = 1,
4258 BusOverLimit = 2,
4260 BusUnderLimit = 3,
4262 PowerOverLimit = 4,
4264}
4265
4266#[repr(C)]
4268#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4269pub struct PamojaIna226Config {
4270 pub reset: u8,
4272 pub averaging: u8,
4274 pub bus_conversion_time: u8,
4276 pub shunt_conversion_time: u8,
4278 pub mode: u8,
4280}
4281
4282#[repr(C)]
4284#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4285pub struct PamojaIna226MaskEnable {
4286 pub shunt_over_limit: u8,
4288 pub shunt_under_limit: u8,
4290 pub bus_over_limit: u8,
4292 pub bus_under_limit: u8,
4294 pub power_over_limit: u8,
4296 pub conversion_ready: u8,
4298 pub alert_function_flag: u8,
4300 pub conversion_ready_flag: u8,
4302 pub math_overflow: u8,
4304 pub alert_active_high: u8,
4306 pub alert_latch: u8,
4308}
4309
4310#[repr(C)]
4312#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4313pub struct PamojaIna226DieId {
4314 pub device: u16,
4316 pub revision: u8,
4318}
4319
4320#[no_mangle]
4331pub unsafe extern "C" fn pamoja_ina226_address(
4332 a1: u8,
4333 a0: u8,
4334 out_address: *mut u8,
4335) -> PamojaStatus {
4336 if out_address.is_null() {
4337 set_last_error("out_address must not be null".to_owned());
4338 return PamojaStatus::InvalidArgument;
4339 }
4340 let (Some(a1), Some(a0)) = (address_pin(a1), address_pin(a0)) else {
4341 set_last_error(
4342 "an INA226 address pin must be tied to GND, VS, SDA, or SCL: code 0 to 3".to_owned(),
4343 );
4344 return PamojaStatus::InvalidArgument;
4345 };
4346 *out_address = ina226::address(a1, a0);
4347 PamojaStatus::Ok
4348}
4349
4350#[no_mangle]
4356pub extern "C" fn pamoja_ina226_averaging_samples(code: u8) -> u16 {
4357 averaging(code).samples()
4358}
4359
4360#[no_mangle]
4366pub extern "C" fn pamoja_ina226_conversion_micros(code: u8) -> u32 {
4367 conversion_time_setting(code).microseconds()
4368}
4369
4370#[no_mangle]
4376pub extern "C" fn pamoja_ina226_measures_shunt(code: u8) -> bool {
4377 mode(code).measures_shunt()
4378}
4379
4380#[no_mangle]
4386pub extern "C" fn pamoja_ina226_measures_bus(code: u8) -> bool {
4387 mode(code).measures_bus()
4388}
4389
4390#[no_mangle]
4396pub extern "C" fn pamoja_ina226_is_continuous(code: u8) -> bool {
4397 mode(code).is_continuous()
4398}
4399
4400#[no_mangle]
4411pub unsafe extern "C" fn pamoja_ina226_config_from_register(
4412 raw: u16,
4413 out_config: *mut PamojaIna226Config,
4414) -> PamojaStatus {
4415 if out_config.is_null() {
4416 set_last_error("out_config must not be null".to_owned());
4417 return PamojaStatus::InvalidArgument;
4418 }
4419 *out_config = ina226::Configuration::from_register(raw).into();
4420 PamojaStatus::Ok
4421}
4422
4423#[no_mangle]
4429pub extern "C" fn pamoja_ina226_config_to_register(config: PamojaIna226Config) -> u16 {
4430 ina226::Configuration::from(config).to_register()
4431}
4432
4433#[no_mangle]
4439pub extern "C" fn pamoja_ina226_update_micros(config: PamojaIna226Config) -> u32 {
4440 ina226::Configuration::from(config).update_microseconds()
4441}
4442
4443#[no_mangle]
4454pub unsafe extern "C" fn pamoja_ina226_mask_enable_from_register(
4455 raw: u16,
4456 out_mask: *mut PamojaIna226MaskEnable,
4457) -> PamojaStatus {
4458 if out_mask.is_null() {
4459 set_last_error("out_mask must not be null".to_owned());
4460 return PamojaStatus::InvalidArgument;
4461 }
4462 *out_mask = ina226::MaskEnable::from_register(raw).into();
4463 PamojaStatus::Ok
4464}
4465
4466#[no_mangle]
4472pub extern "C" fn pamoja_ina226_mask_enable_to_register(mask: PamojaIna226MaskEnable) -> u16 {
4473 ina226::MaskEnable::from(mask).to_register()
4474}
4475
4476#[no_mangle]
4487pub unsafe extern "C" fn pamoja_ina226_active_alert_function(
4488 mask: PamojaIna226MaskEnable,
4489 out_function: *mut PamojaIna226AlertFunction,
4490) -> bool {
4491 match ina226::MaskEnable::from(mask).active_alert_function() {
4492 Some(function) if !out_function.is_null() => {
4493 *out_function = function.into();
4494 true
4495 }
4496 _ => false,
4497 }
4498}
4499
4500#[no_mangle]
4511pub unsafe extern "C" fn pamoja_ina226_die_id(
4512 raw: u16,
4513 out_die_id: *mut PamojaIna226DieId,
4514) -> PamojaStatus {
4515 if out_die_id.is_null() {
4516 set_last_error("out_die_id must not be null".to_owned());
4517 return PamojaStatus::InvalidArgument;
4518 }
4519 *out_die_id = ina226::DieId::from_register(raw).into();
4520 PamojaStatus::Ok
4521}
4522
4523#[no_mangle]
4536pub unsafe extern "C" fn pamoja_ina226_identify(
4537 manufacturer_id: u16,
4538 die_id: u16,
4539 out_die_id: *mut PamojaIna226DieId,
4540) -> PamojaStatus {
4541 if out_die_id.is_null() {
4542 set_last_error("out_die_id must not be null".to_owned());
4543 return PamojaStatus::InvalidArgument;
4544 }
4545 match ina226::identify(manufacturer_id, die_id) {
4546 Ok(identified) => {
4547 *out_die_id = identified.into();
4548 PamojaStatus::Ok
4549 }
4550 Err(error) => failed(error),
4551 }
4552}
4553
4554#[no_mangle]
4560pub extern "C" fn pamoja_ina226_calibration(
4561 current_lsb_microamps: u32,
4562 shunt_milliohms: u32,
4563) -> u16 {
4564 ina226::calibration(current_lsb_microamps, shunt_milliohms)
4565}
4566
4567#[no_mangle]
4573pub extern "C" fn pamoja_ina226_minimum_current_lsb_microamps(max_expected_microamps: u32) -> u32 {
4574 ina226::minimum_current_lsb_microamps(max_expected_microamps)
4575}
4576
4577#[no_mangle]
4583pub extern "C" fn pamoja_ina226_shunt_nanovolts(raw: i16) -> i32 {
4584 ina226::shunt_nanovolts(raw)
4585}
4586
4587#[no_mangle]
4593pub extern "C" fn pamoja_ina226_shunt_millivolts(raw: i16) -> f32 {
4594 ina226::shunt_millivolts_f32(raw)
4595}
4596
4597#[no_mangle]
4603pub extern "C" fn pamoja_ina226_bus_microvolts(raw: u16) -> u32 {
4604 ina226::bus_microvolts(raw)
4605}
4606
4607#[no_mangle]
4613pub extern "C" fn pamoja_ina226_bus_volts(raw: u16) -> f32 {
4614 ina226::bus_volts_f32(raw)
4615}
4616
4617#[no_mangle]
4623pub extern "C" fn pamoja_ina226_current_microamps(raw: i16, current_lsb_microamps: u32) -> i32 {
4624 ina226::current_microamps(raw, current_lsb_microamps)
4625}
4626
4627#[no_mangle]
4633pub extern "C" fn pamoja_ina226_current_amps(raw: i16, current_lsb_microamps: u32) -> f32 {
4634 ina226::current_amps_f32(raw, current_lsb_microamps)
4635}
4636
4637#[no_mangle]
4643pub extern "C" fn pamoja_ina226_power_microwatts(raw: u16, current_lsb_microamps: u32) -> u32 {
4644 ina226::power_microwatts(raw, current_lsb_microamps)
4645}
4646
4647#[no_mangle]
4653pub extern "C" fn pamoja_ina226_power_watts(raw: u16, current_lsb_microamps: u32) -> f32 {
4654 ina226::power_watts_f32(raw, current_lsb_microamps)
4655}
4656
4657#[no_mangle]
4663pub extern "C" fn pamoja_ina226_shunt_register(nanovolts: i32) -> i16 {
4664 ina226::shunt_register(nanovolts)
4665}
4666
4667#[no_mangle]
4673pub extern "C" fn pamoja_ina226_bus_register(microvolts: u32) -> u16 {
4674 ina226::bus_register(microvolts)
4675}
4676
4677#[no_mangle]
4683pub extern "C" fn pamoja_ina226_current_register(
4684 microamps: i32,
4685 current_lsb_microamps: u32,
4686) -> i16 {
4687 ina226::current_register(microamps, current_lsb_microamps)
4688}
4689
4690#[no_mangle]
4696pub extern "C" fn pamoja_ina226_power_register(microwatts: u32, current_lsb_microamps: u32) -> u16 {
4697 ina226::power_register(microwatts, current_lsb_microamps)
4698}
4699
4700#[no_mangle]
4706pub extern "C" fn pamoja_ina226_current_register_from_shunt(shunt: i16, calibration: u16) -> i16 {
4707 ina226::current_register_from_shunt(shunt, calibration)
4708}
4709
4710#[no_mangle]
4716pub extern "C" fn pamoja_ina226_power_register_from_current(current: i16, bus: u16) -> u16 {
4717 ina226::power_register_from_current(current, bus)
4718}
4719
4720impl From<PamojaAds1115Config> for ads1115::Config {
4721 fn from(value: PamojaAds1115Config) -> Self {
4722 ads1115::Config {
4723 start_conversion: value.start_conversion != 0,
4724 mux: ads1115::Mux::from_code(value.mux),
4725 pga: ads1115::Pga::from_code(value.pga),
4726 mode: if value.single_shot != 0 {
4727 ads1115::Mode::SingleShot
4728 } else {
4729 ads1115::Mode::Continuous
4730 },
4731 data_rate: ads1115::DataRate::from_code(value.data_rate),
4732 comparator_mode: if value.window_comparator != 0 {
4733 ads1115::ComparatorMode::Window
4734 } else {
4735 ads1115::ComparatorMode::Traditional
4736 },
4737 comparator_polarity: if value.comparator_active_high != 0 {
4738 ads1115::ComparatorPolarity::ActiveHigh
4739 } else {
4740 ads1115::ComparatorPolarity::ActiveLow
4741 },
4742 comparator_latch: if value.comparator_latching != 0 {
4743 ads1115::ComparatorLatch::Latching
4744 } else {
4745 ads1115::ComparatorLatch::NonLatching
4746 },
4747 comparator_queue: ads1115::ComparatorQueue::from_code(value.comparator_queue),
4748 }
4749 }
4750}
4751
4752impl From<ads1115::Config> for PamojaAds1115Config {
4753 fn from(value: ads1115::Config) -> Self {
4754 PamojaAds1115Config {
4755 start_conversion: u8::from(value.start_conversion),
4756 mux: value.mux.code(),
4757 pga: value.pga.code(),
4758 single_shot: u8::from(matches!(value.mode, ads1115::Mode::SingleShot)),
4759 data_rate: value.data_rate.code(),
4760 window_comparator: u8::from(matches!(
4761 value.comparator_mode,
4762 ads1115::ComparatorMode::Window
4763 )),
4764 comparator_active_high: u8::from(matches!(
4765 value.comparator_polarity,
4766 ads1115::ComparatorPolarity::ActiveHigh
4767 )),
4768 comparator_latching: u8::from(matches!(
4769 value.comparator_latch,
4770 ads1115::ComparatorLatch::Latching
4771 )),
4772 comparator_queue: value.comparator_queue.code(),
4773 }
4774 }
4775}
4776
4777impl From<bmp280::Calibration> for PamojaBmp280Coefficients {
4778 fn from(value: bmp280::Calibration) -> Self {
4779 PamojaBmp280Coefficients {
4780 dig_t1: value.dig_t1,
4781 dig_t2: value.dig_t2,
4782 dig_t3: value.dig_t3,
4783 dig_p1: value.dig_p1,
4784 dig_p2: value.dig_p2,
4785 dig_p3: value.dig_p3,
4786 dig_p4: value.dig_p4,
4787 dig_p5: value.dig_p5,
4788 dig_p6: value.dig_p6,
4789 dig_p7: value.dig_p7,
4790 dig_p8: value.dig_p8,
4791 dig_p9: value.dig_p9,
4792 }
4793 }
4794}
4795
4796impl From<bmp280::Measurement> for PamojaBmp280Measurement {
4797 fn from(value: bmp280::Measurement) -> Self {
4798 PamojaBmp280Measurement {
4799 pressure: value.pressure,
4800 temperature: value.temperature,
4801 pressure_skipped: u8::from(value.pressure_skipped()),
4802 temperature_skipped: u8::from(value.temperature_skipped()),
4803 }
4804 }
4805}
4806
4807impl From<PamojaBme280CtrlMeas> for bme280::CtrlMeas {
4808 fn from(value: PamojaBme280CtrlMeas) -> Self {
4809 bme280::CtrlMeas {
4810 temperature: bme280::Oversampling::from_code(value.temperature),
4811 pressure: bme280::Oversampling::from_code(value.pressure),
4812 mode: bme280::Mode::from_code(value.mode),
4813 }
4814 }
4815}
4816
4817impl From<bme280::CtrlMeas> for PamojaBme280CtrlMeas {
4818 fn from(value: bme280::CtrlMeas) -> Self {
4819 PamojaBme280CtrlMeas {
4820 temperature: value.temperature.code(),
4821 pressure: value.pressure.code(),
4822 mode: value.mode.code(),
4823 }
4824 }
4825}
4826
4827impl From<PamojaBme280Config> for bme280::Config {
4828 fn from(value: PamojaBme280Config) -> Self {
4829 bme280::Config {
4830 standby: bme280::Standby::from_code(value.standby),
4831 filter: bme280::Filter::from_code(value.filter),
4832 spi_3wire: value.spi_3wire != 0,
4833 }
4834 }
4835}
4836
4837impl From<bme280::Config> for PamojaBme280Config {
4838 fn from(value: bme280::Config) -> Self {
4839 PamojaBme280Config {
4840 standby: value.standby.code(),
4841 filter: value.filter.code(),
4842 spi_3wire: u8::from(value.spi_3wire),
4843 }
4844 }
4845}
4846
4847impl From<PamojaBmp280CtrlMeas> for bmp280::CtrlMeas {
4848 fn from(value: PamojaBmp280CtrlMeas) -> Self {
4849 bmp280::CtrlMeas {
4850 temperature: bmp280::Oversampling::from_code(value.temperature),
4851 pressure: bmp280::Oversampling::from_code(value.pressure),
4852 mode: bmp280::Mode::from_code(value.mode),
4853 }
4854 }
4855}
4856
4857impl From<bmp280::CtrlMeas> for PamojaBmp280CtrlMeas {
4858 fn from(value: bmp280::CtrlMeas) -> Self {
4859 PamojaBmp280CtrlMeas {
4860 temperature: value.temperature.code(),
4861 pressure: value.pressure.code(),
4862 mode: value.mode.code(),
4863 }
4864 }
4865}
4866
4867impl From<PamojaBmp280Config> for bmp280::Config {
4868 fn from(value: PamojaBmp280Config) -> Self {
4869 bmp280::Config {
4870 standby: bmp280::Standby::from_code(value.standby),
4871 filter: value.filter,
4872 spi_3wire: value.spi_3wire != 0,
4873 }
4874 }
4875}
4876
4877impl From<bmp280::Config> for PamojaBmp280Config {
4878 fn from(value: bmp280::Config) -> Self {
4879 PamojaBmp280Config {
4880 standby: value.standby.code(),
4881 filter: value.filter,
4882 spi_3wire: u8::from(value.spi_3wire),
4883 }
4884 }
4885}
4886
4887impl From<sht3x::Measurement> for PamojaSht3xMeasurement {
4888 fn from(value: sht3x::Measurement) -> Self {
4889 PamojaSht3xMeasurement {
4890 temperature_raw: value.temperature_raw,
4891 humidity_raw: value.humidity_raw,
4892 milli_celsius: value.temperature_milli_celsius(),
4893 celsius: value.temperature_celsius(),
4894 milli_fahrenheit: value.temperature_milli_fahrenheit(),
4895 fahrenheit: value.temperature_fahrenheit(),
4896 milli_percent: value.humidity_milli_percent(),
4897 relative_humidity: value.relative_humidity(),
4898 }
4899 }
4900}
4901
4902impl From<sht3x::Status> for PamojaSht3xStatus {
4903 fn from(value: sht3x::Status) -> Self {
4904 PamojaSht3xStatus {
4905 bits: value.bits(),
4906 alert_pending: u8::from(value.alert_pending()),
4907 heater_on: u8::from(value.heater_on()),
4908 humidity_tracking_alert: u8::from(value.humidity_tracking_alert()),
4909 temperature_tracking_alert: u8::from(value.temperature_tracking_alert()),
4910 reset_detected: u8::from(value.reset_detected()),
4911 command_failed: u8::from(value.command_failed()),
4912 write_checksum_failed: u8::from(value.write_checksum_failed()),
4913 }
4914 }
4915}
4916
4917impl From<scd4x::Measurement> for PamojaScd4xMeasurement {
4918 fn from(value: scd4x::Measurement) -> Self {
4919 PamojaScd4xMeasurement {
4920 co2_ppm: value.co2_ppm,
4921 temperature_raw: value.temperature_raw,
4922 humidity_raw: value.humidity_raw,
4923 milli_celsius: value.milli_celsius(),
4924 celsius: value.celsius(),
4925 humidity_milli_percent: value.humidity_milli_percent(),
4926 relative_humidity_percent: value.relative_humidity_percent(),
4927 }
4928 }
4929}
4930
4931impl From<PamojaTmp117Config> for tmp117::Configuration {
4932 fn from(value: PamojaTmp117Config) -> Self {
4933 tmp117::Configuration {
4934 high_alert: value.high_alert != 0,
4935 low_alert: value.low_alert != 0,
4936 data_ready: value.data_ready != 0,
4937 eeprom_busy: value.eeprom_busy != 0,
4938 mode: tmp117::ConversionMode::from_code(value.mode),
4939 cycle: tmp117::ConversionCycle::from_code(value.cycle),
4940 averaging: tmp117::Averaging::from_code(value.averaging),
4941 alert_mode: if value.therm_mode != 0 {
4942 tmp117::AlertMode::Therm
4943 } else {
4944 tmp117::AlertMode::Alert
4945 },
4946 alert_polarity: if value.alert_active_high != 0 {
4947 tmp117::AlertPolarity::ActiveHigh
4948 } else {
4949 tmp117::AlertPolarity::ActiveLow
4950 },
4951 alert_pin: if value.alert_pin_data_ready != 0 {
4952 tmp117::AlertPin::DataReady
4953 } else {
4954 tmp117::AlertPin::AlertFlags
4955 },
4956 soft_reset: value.soft_reset != 0,
4957 }
4958 }
4959}
4960
4961impl From<tmp117::Configuration> for PamojaTmp117Config {
4962 fn from(value: tmp117::Configuration) -> Self {
4963 PamojaTmp117Config {
4964 high_alert: u8::from(value.high_alert),
4965 low_alert: u8::from(value.low_alert),
4966 data_ready: u8::from(value.data_ready),
4967 eeprom_busy: u8::from(value.eeprom_busy),
4968 mode: value.mode.code(),
4969 cycle: value.cycle.code(),
4970 averaging: value.averaging.code(),
4971 therm_mode: u8::from(matches!(value.alert_mode, tmp117::AlertMode::Therm)),
4972 alert_active_high: u8::from(matches!(
4973 value.alert_polarity,
4974 tmp117::AlertPolarity::ActiveHigh
4975 )),
4976 alert_pin_data_ready: u8::from(matches!(value.alert_pin, tmp117::AlertPin::DataReady)),
4977 soft_reset: u8::from(value.soft_reset),
4978 }
4979 }
4980}
4981
4982impl From<hdc1080::Measurement> for PamojaHdc1080Measurement {
4983 fn from(value: hdc1080::Measurement) -> Self {
4984 PamojaHdc1080Measurement {
4985 temperature_raw: value.temperature,
4986 humidity_raw: value.humidity,
4987 milli_celsius: value.milli_celsius(),
4988 celsius: value.celsius(),
4989 milli_percent: value.milli_percent(),
4990 relative_humidity: value.relative_humidity(),
4991 }
4992 }
4993}
4994
4995impl From<hdc1080::Configuration> for PamojaHdc1080Config {
4996 fn from(value: hdc1080::Configuration) -> Self {
4997 PamojaHdc1080Config {
4998 software_reset: u8::from(value.software_reset),
4999 heater: u8::from(value.heater),
5000 sequential: u8::from(matches!(
5001 value.mode,
5002 hdc1080::AcquisitionMode::TemperatureThenHumidity
5003 )),
5004 battery_low: u8::from(value.battery_low),
5005 temperature_resolution_bits: match value.temperature_resolution {
5006 hdc1080::TemperatureResolution::Bits14 => 14,
5007 hdc1080::TemperatureResolution::Bits11 => 11,
5008 },
5009 humidity_resolution_bits: match value.humidity_resolution {
5010 hdc1080::HumidityResolution::Bits14 => 14,
5011 hdc1080::HumidityResolution::Bits11 => 11,
5012 hdc1080::HumidityResolution::Bits8 => 8,
5013 },
5014 }
5015 }
5016}
5017
5018impl TryFrom<PamojaHdc1080Config> for hdc1080::Configuration {
5019 type Error = PamojaStatus;
5020
5021 fn try_from(value: PamojaHdc1080Config) -> Result<Self, PamojaStatus> {
5022 let Some(temperature_resolution) =
5023 temperature_resolution(value.temperature_resolution_bits)
5024 else {
5025 return Err(bad_temperature_resolution());
5026 };
5027 let Some(humidity_resolution) = humidity_resolution(value.humidity_resolution_bits) else {
5028 return Err(bad_humidity_resolution());
5029 };
5030 Ok(hdc1080::Configuration {
5031 software_reset: value.software_reset != 0,
5032 heater: value.heater != 0,
5033 mode: if value.sequential != 0 {
5034 hdc1080::AcquisitionMode::TemperatureThenHumidity
5035 } else {
5036 hdc1080::AcquisitionMode::Single
5037 },
5038 battery_low: value.battery_low != 0,
5039 temperature_resolution,
5040 humidity_resolution,
5041 })
5042 }
5043}
5044
5045impl From<PamojaOpt3001Config> for opt3001::Configuration {
5046 fn from(value: PamojaOpt3001Config) -> Self {
5047 opt3001::Configuration {
5048 range_number: value.range_number,
5049 conversion_time: conversion_time(value.long_conversion != 0),
5050 mode: opt3001::Mode::from_code(value.mode),
5051 overflow: value.overflow != 0,
5052 conversion_ready: value.conversion_ready != 0,
5053 flag_high: value.flag_high != 0,
5054 flag_low: value.flag_low != 0,
5055 latch: if value.latched_window != 0 {
5056 opt3001::Latch::LatchedWindow
5057 } else {
5058 opt3001::Latch::TransparentHysteresis
5059 },
5060 polarity: if value.active_high != 0 {
5061 opt3001::Polarity::ActiveHigh
5062 } else {
5063 opt3001::Polarity::ActiveLow
5064 },
5065 mask_exponent: value.mask_exponent != 0,
5066 fault_count: opt3001::FaultCount::from_code(value.fault_count),
5067 }
5068 }
5069}
5070
5071impl From<opt3001::Configuration> for PamojaOpt3001Config {
5072 fn from(value: opt3001::Configuration) -> Self {
5073 PamojaOpt3001Config {
5074 range_number: value.range_number,
5075 long_conversion: u8::from(matches!(
5076 value.conversion_time,
5077 opt3001::ConversionTime::Ms800
5078 )),
5079 mode: value.mode.code(),
5080 overflow: u8::from(value.overflow),
5081 conversion_ready: u8::from(value.conversion_ready),
5082 flag_high: u8::from(value.flag_high),
5083 flag_low: u8::from(value.flag_low),
5084 latched_window: u8::from(matches!(value.latch, opt3001::Latch::LatchedWindow)),
5085 active_high: u8::from(matches!(value.polarity, opt3001::Polarity::ActiveHigh)),
5086 mask_exponent: u8::from(value.mask_exponent),
5087 fault_count: value.fault_count.code(),
5088 }
5089 }
5090}
5091
5092impl From<PamojaIna226Config> for ina226::Configuration {
5093 fn from(value: PamojaIna226Config) -> Self {
5094 ina226::Configuration {
5095 reset: value.reset != 0,
5096 averaging: averaging(value.averaging),
5097 bus_conversion_time: conversion_time_setting(value.bus_conversion_time),
5098 shunt_conversion_time: conversion_time_setting(value.shunt_conversion_time),
5099 mode: mode(value.mode),
5100 }
5101 }
5102}
5103
5104impl From<ina226::Configuration> for PamojaIna226Config {
5105 fn from(value: ina226::Configuration) -> Self {
5106 PamojaIna226Config {
5107 reset: u8::from(value.reset),
5108 averaging: value.averaging as u8,
5109 bus_conversion_time: value.bus_conversion_time as u8,
5110 shunt_conversion_time: value.shunt_conversion_time as u8,
5111 mode: value.mode as u8,
5112 }
5113 }
5114}
5115
5116impl From<PamojaIna226MaskEnable> for ina226::MaskEnable {
5117 fn from(value: PamojaIna226MaskEnable) -> Self {
5118 ina226::MaskEnable {
5119 shunt_over_limit: value.shunt_over_limit != 0,
5120 shunt_under_limit: value.shunt_under_limit != 0,
5121 bus_over_limit: value.bus_over_limit != 0,
5122 bus_under_limit: value.bus_under_limit != 0,
5123 power_over_limit: value.power_over_limit != 0,
5124 conversion_ready: value.conversion_ready != 0,
5125 alert_function_flag: value.alert_function_flag != 0,
5126 conversion_ready_flag: value.conversion_ready_flag != 0,
5127 math_overflow: value.math_overflow != 0,
5128 alert_active_high: value.alert_active_high != 0,
5129 alert_latch: value.alert_latch != 0,
5130 }
5131 }
5132}
5133
5134impl From<ina226::MaskEnable> for PamojaIna226MaskEnable {
5135 fn from(value: ina226::MaskEnable) -> Self {
5136 PamojaIna226MaskEnable {
5137 shunt_over_limit: u8::from(value.shunt_over_limit),
5138 shunt_under_limit: u8::from(value.shunt_under_limit),
5139 bus_over_limit: u8::from(value.bus_over_limit),
5140 bus_under_limit: u8::from(value.bus_under_limit),
5141 power_over_limit: u8::from(value.power_over_limit),
5142 conversion_ready: u8::from(value.conversion_ready),
5143 alert_function_flag: u8::from(value.alert_function_flag),
5144 conversion_ready_flag: u8::from(value.conversion_ready_flag),
5145 math_overflow: u8::from(value.math_overflow),
5146 alert_active_high: u8::from(value.alert_active_high),
5147 alert_latch: u8::from(value.alert_latch),
5148 }
5149 }
5150}
5151
5152impl From<ina226::AlertFunction> for PamojaIna226AlertFunction {
5153 fn from(value: ina226::AlertFunction) -> Self {
5154 match value {
5155 ina226::AlertFunction::ShuntOverLimit => Self::ShuntOverLimit,
5156 ina226::AlertFunction::ShuntUnderLimit => Self::ShuntUnderLimit,
5157 ina226::AlertFunction::BusOverLimit => Self::BusOverLimit,
5158 ina226::AlertFunction::BusUnderLimit => Self::BusUnderLimit,
5159 ina226::AlertFunction::PowerOverLimit => Self::PowerOverLimit,
5160 }
5161 }
5162}
5163
5164impl From<ina226::DieId> for PamojaIna226DieId {
5165 fn from(value: ina226::DieId) -> Self {
5166 PamojaIna226DieId {
5167 device: value.device,
5168 revision: value.revision,
5169 }
5170 }
5171}
5172
5173impl From<ds18b20::Scratchpad> for PamojaDs18b20Reading {
5174 fn from(value: ds18b20::Scratchpad) -> Self {
5175 PamojaDs18b20Reading {
5176 raw_temperature: value.raw_temperature(),
5177 micro_celsius: value.temperature_micro_celsius(),
5178 alarm_high: value.alarm_high(),
5179 alarm_low: value.alarm_low(),
5180 resolution_bits: value.resolution().bits(),
5181 }
5182 }
5183}
5184
5185impl From<PamojaIna219Config> for ina219::Configuration {
5186 fn from(value: PamojaIna219Config) -> Self {
5187 ina219::Configuration {
5188 reset: value.reset != 0,
5189 bus_range: ina219::BusRange::from_code(value.bus_range),
5190 gain: ina219::Gain::from_code(value.gain),
5191 bus_adc: ina219::Adc::from_code(value.bus_adc),
5192 shunt_adc: ina219::Adc::from_code(value.shunt_adc),
5193 mode: ina219::Mode::from_code(value.mode),
5194 }
5195 }
5196}
5197
5198impl From<ina219::Configuration> for PamojaIna219Config {
5199 fn from(value: ina219::Configuration) -> Self {
5200 PamojaIna219Config {
5201 reset: u8::from(value.reset),
5202 bus_range: value.bus_range.code(),
5203 gain: value.gain.code(),
5204 bus_adc: value.bus_adc.code(),
5205 shunt_adc: value.shunt_adc.code(),
5206 mode: value.mode.code(),
5207 }
5208 }
5209}
5210
5211pub(crate) fn repeatability_from_code(code: u8) -> Option<sht3x::Repeatability> {
5213 match code {
5214 0 => Some(sht3x::Repeatability::Low),
5215 1 => Some(sht3x::Repeatability::Medium),
5216 2 => Some(sht3x::Repeatability::High),
5217 _ => None,
5218 }
5219}
5220
5221pub(crate) fn bad_repeatability() -> PamojaStatus {
5223 set_last_error("SHT3x repeatability must be 0 low, 1 medium, or 2 high".to_owned());
5224 PamojaStatus::InvalidArgument
5225}
5226
5227fn rate_from_code(code: u8) -> Option<sht3x::Rate> {
5229 match code {
5230 0 => Some(sht3x::Rate::HalfMps),
5231 1 => Some(sht3x::Rate::OneMps),
5232 2 => Some(sht3x::Rate::TwoMps),
5233 3 => Some(sht3x::Rate::FourMps),
5234 4 => Some(sht3x::Rate::TenMps),
5235 _ => None,
5236 }
5237}
5238
5239fn bad_rate() -> PamojaStatus {
5241 set_last_error("SHT3x rate must be 0 for 0.5, 1, 2, 3, or 4 for 10 per second".to_owned());
5242 PamojaStatus::InvalidArgument
5243}
5244
5245pub(crate) fn temperature_resolution(bits: u8) -> Option<hdc1080::TemperatureResolution> {
5247 match bits {
5248 14 => Some(hdc1080::TemperatureResolution::Bits14),
5249 11 => Some(hdc1080::TemperatureResolution::Bits11),
5250 _ => None,
5251 }
5252}
5253
5254pub(crate) fn bad_temperature_resolution() -> PamojaStatus {
5256 set_last_error("HDC1080 temperature resolution must be 14 or 11 bits".to_owned());
5257 PamojaStatus::InvalidArgument
5258}
5259
5260pub(crate) fn humidity_resolution(bits: u8) -> Option<hdc1080::HumidityResolution> {
5262 match bits {
5263 14 => Some(hdc1080::HumidityResolution::Bits14),
5264 11 => Some(hdc1080::HumidityResolution::Bits11),
5265 8 => Some(hdc1080::HumidityResolution::Bits8),
5266 _ => None,
5267 }
5268}
5269
5270pub(crate) fn bad_humidity_resolution() -> PamojaStatus {
5272 set_last_error("HDC1080 humidity resolution must be 14, 11, or 8 bits".to_owned());
5273 PamojaStatus::InvalidArgument
5274}
5275
5276pub(crate) fn conversion_time(long_conversion: bool) -> opt3001::ConversionTime {
5278 if long_conversion {
5279 opt3001::ConversionTime::Ms800
5280 } else {
5281 opt3001::ConversionTime::Ms100
5282 }
5283}
5284
5285pub(crate) fn averaging(code: u8) -> ina226::Averaging {
5287 ina226::Configuration::from_register(u16::from(code & 0x07) << 9).averaging
5288}
5289
5290pub(crate) fn conversion_time_setting(code: u8) -> ina226::ConversionTime {
5292 ina226::Configuration::from_register(u16::from(code & 0x07) << 6).bus_conversion_time
5293}
5294
5295pub(crate) fn mode(code: u8) -> ina226::Mode {
5297 ina226::Configuration::from_register(u16::from(code & 0x07)).mode
5298}
5299
5300fn address_pin(code: u8) -> Option<ina226::AddressPin> {
5302 match code {
5303 0 => Some(ina226::AddressPin::Ground),
5304 1 => Some(ina226::AddressPin::Supply),
5305 2 => Some(ina226::AddressPin::Sda),
5306 3 => Some(ina226::AddressPin::Scl),
5307 _ => None,
5308 }
5309}
5310
5311fn resolution(bits: u8) -> Option<ds18b20::Resolution> {
5313 match bits {
5314 9 => Some(ds18b20::Resolution::Bits9),
5315 10 => Some(ds18b20::Resolution::Bits10),
5316 11 => Some(ds18b20::Resolution::Bits11),
5317 12 => Some(ds18b20::Resolution::Bits12),
5318 _ => None,
5319 }
5320}
5321
5322fn bad_resolution() -> PamojaStatus {
5324 set_last_error("DS18B20 resolution must be 9, 10, 11, or 12 bits".to_owned());
5325 PamojaStatus::InvalidArgument
5326}
5327
5328fn wrong_length(what: &str, expected: usize) -> PamojaStatus {
5330 set_last_error(format!("{what} must be exactly {expected} bytes"));
5331 PamojaStatus::InvalidArgument
5332}
5333
5334fn failed(error: SensorError) -> PamojaStatus {
5336 set_last_error(error.to_string());
5337 PamojaStatus::Codec
5338}
5339
5340#[cfg(test)]
5341mod tests {
5342 use super::*;
5343 use std::ptr;
5344
5345 #[test]
5346 fn a_bme280_burst_read_compensates_to_a_reading() {
5347 let temp_press = [0u8; PAMOJA_BME280_CALIBRATION_TEMP_PRESS_LEN];
5349 let humidity = [0u8; PAMOJA_BME280_CALIBRATION_HUMIDITY_LEN];
5350 let measurement = [0u8; PAMOJA_BME280_MEASUREMENT_LEN];
5351 let mut calibration = ptr::null_mut();
5352 let mut reading = PamojaBme280Measurement {
5353 celsius: 0.0,
5354 pascals: 0,
5355 hectopascals: 0.0,
5356 relative_humidity_percent: 0.0,
5357 };
5358
5359 unsafe {
5361 assert_eq!(
5362 pamoja_bme280_calibration_new(
5363 temp_press.as_ptr(),
5364 temp_press.len(),
5365 humidity.as_ptr(),
5366 humidity.len(),
5367 &mut calibration
5368 ),
5369 PamojaStatus::Ok
5370 );
5371 assert_eq!(
5372 pamoja_bme280_compensate(
5373 calibration,
5374 measurement.as_ptr(),
5375 measurement.len(),
5376 &mut reading
5377 ),
5378 PamojaStatus::Ok
5379 );
5380 pamoja_bme280_calibration_free(calibration);
5381 }
5382 assert!(reading.hectopascals.is_finite());
5383 }
5384
5385 #[test]
5386 fn a_calibration_of_the_wrong_length_is_refused() {
5387 let short = [0u8; 4];
5388 let humidity = [0u8; PAMOJA_BME280_CALIBRATION_HUMIDITY_LEN];
5389 let mut calibration = ptr::null_mut();
5390 let status = unsafe {
5392 pamoja_bme280_calibration_new(
5393 short.as_ptr(),
5394 short.len(),
5395 humidity.as_ptr(),
5396 humidity.len(),
5397 &mut calibration,
5398 )
5399 };
5400 assert_eq!(status, PamojaStatus::InvalidArgument);
5401 assert!(calibration.is_null());
5402 }
5403
5404 #[test]
5405 fn a_scratchpad_decodes_and_its_crc_is_checked() {
5406 let mut bytes = [0x91u8, 0x01, 0x4B, 0x46, 0x7F, 0xFF, 0x0C, 0x10, 0x00];
5408 bytes[8] = unsafe { pamoja_ds18b20_crc8(bytes.as_ptr(), 8) };
5410
5411 let mut reading = PamojaDs18b20Reading {
5412 raw_temperature: 0,
5413 micro_celsius: 0,
5414 alarm_high: 0,
5415 alarm_low: 0,
5416 resolution_bits: 0,
5417 };
5418 unsafe {
5420 assert_eq!(
5421 pamoja_ds18b20_parse_scratchpad(bytes.as_ptr(), bytes.len(), &mut reading),
5422 PamojaStatus::Ok
5423 );
5424 }
5425 assert_eq!(reading.raw_temperature, 0x0191);
5426 assert_eq!(reading.micro_celsius, 25_062_500);
5427 assert_eq!(reading.resolution_bits, 12);
5428
5429 bytes[0] ^= 0xFF;
5430 let status =
5432 unsafe { pamoja_ds18b20_parse_scratchpad(bytes.as_ptr(), bytes.len(), &mut reading) };
5433 assert_eq!(
5434 status,
5435 PamojaStatus::Codec,
5436 "a read corrupted on the bus must not be trusted"
5437 );
5438 }
5439
5440 #[test]
5441 fn a_resolution_outside_the_datasheet_is_refused() {
5442 let mut byte = 0u8;
5443 let status = unsafe { pamoja_ds18b20_config_byte(8, &mut byte) };
5445 assert_eq!(status, PamojaStatus::InvalidArgument);
5446 }
5447
5448 #[test]
5449 fn the_resolution_round_trips_through_its_config_byte() {
5450 for bits in [9u8, 10, 11, 12] {
5451 let mut byte = 0u8;
5452 unsafe {
5454 assert_eq!(
5455 pamoja_ds18b20_config_byte(bits, &mut byte),
5456 PamojaStatus::Ok
5457 );
5458 }
5459 assert_eq!(pamoja_ds18b20_resolution_bits(byte), bits);
5460 }
5461 }
5462
5463 #[test]
5464 fn an_ina219_reading_converts_at_its_calibrated_resolution() {
5465 const CURRENT_LSB: u32 = 1_000;
5468 assert_eq!(pamoja_ina219_calibration(CURRENT_LSB, 2), 0x5000);
5469 assert_eq!(
5470 pamoja_ina219_minimum_current_lsb_microamps(15_000_000),
5471 458,
5472 "15 A over a 15-bit register, rounded up to the next whole microamp"
5473 );
5474 assert_eq!(
5475 pamoja_ina219_current_microamps(1_000, CURRENT_LSB),
5476 1_000_000
5477 );
5478 assert_eq!(pamoja_ina219_power_microwatts(100, CURRENT_LSB), 2_000_000);
5480 assert!(pamoja_ina219_conversion_ready(0x0002));
5481 assert!(pamoja_ina219_math_overflow(0x0001));
5482 }
5483
5484 #[test]
5485 fn an_ads1115_config_round_trips_through_its_register() {
5486 let config = PamojaAds1115Config {
5487 start_conversion: 1,
5488 mux: 4,
5489 pga: 1,
5490 single_shot: 1,
5491 data_rate: 4,
5492 window_comparator: 0,
5493 comparator_active_high: 0,
5494 comparator_latching: 0,
5495 comparator_queue: 3,
5496 };
5497 let bits = pamoja_ads1115_config_bits(config);
5498 let mut back = config;
5499 unsafe {
5501 assert_eq!(
5502 pamoja_ads1115_config_from_bits(bits, &mut back),
5503 PamojaStatus::Ok
5504 );
5505 }
5506 assert_eq!(back, config);
5507 }
5508
5509 #[test]
5510 fn an_ads1115_conversion_scales_to_its_full_range() {
5511 assert_eq!(pamoja_ads1115_full_scale_microvolts(1), 4_096_000);
5513 assert_eq!(pamoja_ads1115_to_nanovolts(1, 32_767), 4_095_875_000);
5514 assert!((pamoja_ads1115_to_volts(1, 0)).abs() < f32::EPSILON);
5515 }
5516
5517 #[test]
5518 fn a_bmp280_burst_read_compensates_and_its_coefficients_round_trip() {
5519 let calibration_bytes = [
5521 0x70, 0x6B, 0x43, 0x67, 0x18, 0xFC, 0x7D, 0x8E, 0x43, 0xD6, 0xD0, 0x0B, 0x27, 0x0B,
5522 0x8C, 0x00, 0xF9, 0xFF, 0x8C, 0x3C, 0xF8, 0xC6, 0x70, 0x17,
5523 ];
5524 let measurement = [0x65u8, 0x5A, 0xC0, 0x7E, 0xED, 0x00];
5525 let mut calibration = ptr::null_mut();
5526 let mut reading = PamojaBmp280Reading {
5527 celsius: 0.0,
5528 pascals: 0,
5529 hectopascals: 0.0,
5530 };
5531 let mut coefficients = [0u8; PAMOJA_BMP280_CALIBRATION_LEN];
5532
5533 unsafe {
5535 assert_eq!(
5536 pamoja_bmp280_calibration_new(
5537 calibration_bytes.as_ptr(),
5538 calibration_bytes.len(),
5539 &mut calibration
5540 ),
5541 PamojaStatus::Ok
5542 );
5543 assert_eq!(
5544 pamoja_bmp280_compensate(
5545 calibration,
5546 measurement.as_ptr(),
5547 measurement.len(),
5548 &mut reading
5549 ),
5550 PamojaStatus::Ok
5551 );
5552 assert_eq!(
5553 pamoja_bmp280_calibration_to_bytes(calibration, coefficients.as_mut_ptr()),
5554 PamojaStatus::Ok
5555 );
5556 pamoja_bmp280_calibration_free(calibration);
5557 }
5558 assert_eq!(reading.pascals, 100_653);
5559 assert!((reading.celsius - 25.08).abs() < 1e-2);
5560 assert_eq!(
5561 coefficients, calibration_bytes,
5562 "the coefficients round-trip through their registers"
5563 );
5564 }
5565
5566 #[test]
5567 fn a_bmp280_control_register_round_trips() {
5568 let ctrl = PamojaBmp280CtrlMeas {
5569 temperature: 2,
5570 pressure: 5,
5571 mode: 3,
5572 };
5573 let bits = pamoja_bmp280_ctrl_meas_bits(ctrl);
5574 let mut back = ctrl;
5575 unsafe {
5577 assert_eq!(
5578 pamoja_bmp280_ctrl_meas_from_bits(bits, &mut back),
5579 PamojaStatus::Ok
5580 );
5581 }
5582 assert_eq!(back, ctrl);
5583 assert_eq!(pamoja_bmp280_oversampling_factor(5), 16);
5584 assert!(pamoja_bmp280_measuring(0x08));
5585 }
5586
5587 #[test]
5588 fn an_sht3x_frame_decodes_and_its_crc_is_checked() {
5589 let frame = [0x66u8, 0x66, 0x93, 0x99, 0x99, 0xBE];
5591 let mut measurement = PamojaSht3xMeasurement {
5592 temperature_raw: 0,
5593 humidity_raw: 0,
5594 milli_celsius: 0,
5595 celsius: 0.0,
5596 milli_fahrenheit: 0,
5597 fahrenheit: 0.0,
5598 milli_percent: 0,
5599 relative_humidity: 0.0,
5600 };
5601 unsafe {
5603 assert_eq!(
5604 pamoja_sht3x_parse_measurement(frame.as_ptr(), frame.len(), &mut measurement),
5605 PamojaStatus::Ok
5606 );
5607 }
5608 assert_eq!(measurement.milli_celsius, 25_000);
5609 assert_eq!(measurement.milli_fahrenheit, 77_000);
5610 assert_eq!(measurement.milli_percent, 60_000);
5611
5612 let check = [0xBEu8, 0xEF];
5613 assert_eq!(
5615 unsafe { pamoja_sht3x_crc(check.as_ptr(), check.len()) },
5616 0x92,
5617 "Sensirion's published check value"
5618 );
5619
5620 let corrupt = [0x66u8, 0x67, 0x93, 0x99, 0x99, 0xBE];
5621 let status = unsafe {
5623 pamoja_sht3x_parse_measurement(corrupt.as_ptr(), corrupt.len(), &mut measurement)
5624 };
5625 assert_eq!(
5626 status,
5627 PamojaStatus::Codec,
5628 "a read corrupted on the bus must not be trusted"
5629 );
5630 }
5631
5632 #[test]
5633 fn an_sht3x_status_and_command_follow_the_tables() {
5634 let frame = [0x80u8, 0x10, 0xE1];
5635 let mut status = PamojaSht3xStatus {
5636 bits: 0,
5637 alert_pending: 0,
5638 heater_on: 0,
5639 humidity_tracking_alert: 0,
5640 temperature_tracking_alert: 0,
5641 reset_detected: 0,
5642 command_failed: 0,
5643 write_checksum_failed: 0,
5644 };
5645 let mut command = 0u16;
5646 unsafe {
5648 assert_eq!(
5649 pamoja_sht3x_parse_status(frame.as_ptr(), frame.len(), &mut status),
5650 PamojaStatus::Ok
5651 );
5652 assert_eq!(
5653 pamoja_sht3x_single_shot(2, true, &mut command),
5654 PamojaStatus::Ok
5655 );
5656 }
5657 assert_eq!(status.bits, PAMOJA_SHT3X_STATUS_DEFAULT);
5658 assert_eq!(status.alert_pending, 1);
5659 assert_eq!(status.reset_detected, 1);
5660 assert_eq!(status.heater_on, 0);
5661 assert_eq!(command, PAMOJA_SHT3X_COMMAND_SINGLE_SHOT_HIGH_STRETCH);
5662
5663 let status = unsafe { pamoja_sht3x_single_shot(3, true, &mut command) };
5665 assert_eq!(status, PamojaStatus::InvalidArgument);
5666 }
5667
5668 #[test]
5669 fn an_scd4x_frame_decodes_and_its_crc_is_checked() {
5670 let frame = [0x01u8, 0xF4, 0x33, 0x66, 0x67, 0xA2, 0x5E, 0xB9, 0x3C];
5671 let mut measurement = PamojaScd4xMeasurement {
5672 co2_ppm: 0,
5673 temperature_raw: 0,
5674 humidity_raw: 0,
5675 milli_celsius: 0,
5676 celsius: 0.0,
5677 humidity_milli_percent: 0,
5678 relative_humidity_percent: 0.0,
5679 };
5680 unsafe {
5682 assert_eq!(
5683 pamoja_scd4x_parse_measurement(frame.as_ptr(), frame.len(), &mut measurement),
5684 PamojaStatus::Ok
5685 );
5686 }
5687 assert_eq!(measurement.co2_ppm, 500);
5688 assert_eq!(measurement.milli_celsius, 25_003);
5689 assert_eq!(measurement.humidity_milli_percent, 37_002);
5690
5691 let corrupt = [0x01u8, 0xF4, 0xCC, 0x66, 0x67, 0xA2, 0x5E, 0xB9, 0x3C];
5692 let status = unsafe {
5694 pamoja_scd4x_parse_measurement(corrupt.as_ptr(), corrupt.len(), &mut measurement)
5695 };
5696 assert_eq!(status, PamojaStatus::Codec);
5697
5698 assert_eq!(pamoja_scd4x_temperature_offset_word(5_400), 0x07E6);
5700
5701 let serial_frame = [0xF8u8, 0x96, 0x31, 0x9F, 0x07, 0xC2, 0x3B, 0xBE, 0x89];
5702 let mut serial = 0u64;
5703 unsafe {
5705 assert_eq!(
5706 pamoja_scd4x_serial_number(serial_frame.as_ptr(), serial_frame.len(), &mut serial),
5707 PamojaStatus::Ok
5708 );
5709 }
5710 assert_eq!(serial, 273_325_796_834_238);
5711 }
5712
5713 #[test]
5714 fn a_tmp117_register_decodes_to_its_datasheet_row() {
5715 assert_eq!(pamoja_tmp117_micro_celsius(0x0C80), 25_000_000);
5716 assert_eq!(pamoja_tmp117_nano_celsius(-1), -7_812_500);
5717 assert_eq!(pamoja_tmp117_raw_from_micro_celsius(-25_000_000), -3_200);
5718 assert_eq!(pamoja_tmp117_device_id(0x1117), 0x117);
5719 assert_eq!(pamoja_tmp117_revision(0x1117), 1);
5720 assert!(pamoja_tmp117_high_alert(0xA220));
5721 assert!(!pamoja_tmp117_low_alert(0xA220));
5722 assert!(pamoja_tmp117_data_ready(0xA220));
5723
5724 let mut config = PamojaTmp117Config {
5725 high_alert: 0,
5726 low_alert: 0,
5727 data_ready: 0,
5728 eeprom_busy: 0,
5729 mode: 0,
5730 cycle: 0,
5731 averaging: 0,
5732 therm_mode: 0,
5733 alert_active_high: 0,
5734 alert_pin_data_ready: 0,
5735 soft_reset: 0,
5736 };
5737 unsafe {
5739 assert_eq!(
5740 pamoja_tmp117_config_from_bits(PAMOJA_TMP117_CONFIG_RESET, &mut config),
5741 PamojaStatus::Ok
5742 );
5743 }
5744 assert_eq!(
5745 pamoja_tmp117_config_bits(config),
5746 PAMOJA_TMP117_CONFIG_RESET
5747 );
5748 assert_eq!(pamoja_tmp117_cycle_micros(5, 0), 4_000_000);
5749 }
5750
5751 #[test]
5752 fn an_hdc1080_measurement_decodes_and_an_undefined_code_is_refused() {
5753 let bytes = [0x60u8, 0x00, 0x40, 0x00];
5754 let mut measurement = PamojaHdc1080Measurement {
5755 temperature_raw: 0,
5756 humidity_raw: 0,
5757 milli_celsius: 0,
5758 celsius: 0.0,
5759 milli_percent: 0,
5760 relative_humidity: 0.0,
5761 };
5762 let mut config = PamojaHdc1080Config {
5763 software_reset: 0,
5764 heater: 0,
5765 sequential: 0,
5766 battery_low: 0,
5767 temperature_resolution_bits: 0,
5768 humidity_resolution_bits: 0,
5769 };
5770 let mut register = 0u16;
5771 unsafe {
5773 assert_eq!(
5774 pamoja_hdc1080_parse_measurement(bytes.as_ptr(), bytes.len(), &mut measurement),
5775 PamojaStatus::Ok
5776 );
5777 assert_eq!(
5778 pamoja_hdc1080_config_from_register(
5779 PAMOJA_HDC1080_CONFIGURATION_RESET,
5780 &mut config
5781 ),
5782 PamojaStatus::Ok
5783 );
5784 assert_eq!(
5785 pamoja_hdc1080_config_to_register(config, &mut register),
5786 PamojaStatus::Ok
5787 );
5788 }
5789 assert_eq!(measurement.milli_celsius, 21_875);
5790 assert_eq!(measurement.milli_percent, 25_000);
5791 assert_eq!(register, PAMOJA_HDC1080_CONFIGURATION_RESET);
5792
5793 let status = unsafe { pamoja_hdc1080_config_from_register(0x1300, &mut config) };
5795 assert_eq!(
5796 status,
5797 PamojaStatus::Codec,
5798 "the humidity-resolution code the datasheet leaves undefined is refused"
5799 );
5800 }
5801
5802 #[test]
5803 fn an_opt3001_result_decodes_and_a_reserved_range_has_no_full_scale() {
5804 assert_eq!(pamoja_opt3001_milli_lux(0xBFFF), 83_865_600);
5805 assert_eq!(pamoja_opt3001_raw_from_milli_lux(88_800), 0x28AC);
5806
5807 let mut full_scale = 0u32;
5808 unsafe {
5810 assert!(pamoja_opt3001_full_scale_milli_lux(0, &mut full_scale));
5811 assert_eq!(full_scale, 40_950);
5812 assert!(
5813 !pamoja_opt3001_full_scale_milli_lux(
5814 PAMOJA_OPT3001_RANGE_AUTOMATIC,
5815 &mut full_scale
5816 ),
5817 "a reserved range number has no full scale"
5818 );
5819 }
5820
5821 let mut config = PamojaOpt3001Config {
5822 range_number: 0,
5823 long_conversion: 0,
5824 mode: 0,
5825 overflow: 0,
5826 conversion_ready: 0,
5827 flag_high: 0,
5828 flag_low: 0,
5829 latched_window: 0,
5830 active_high: 0,
5831 mask_exponent: 0,
5832 fault_count: 0,
5833 };
5834 unsafe {
5836 assert_eq!(
5837 pamoja_opt3001_config_from_bits(PAMOJA_OPT3001_CONFIGURATION_RESET, &mut config),
5838 PamojaStatus::Ok
5839 );
5840 }
5841 assert_eq!(
5842 pamoja_opt3001_config_bits(config),
5843 PAMOJA_OPT3001_CONFIGURATION_RESET
5844 );
5845 }
5846
5847 #[test]
5848 fn an_ina226_converts_at_its_calibrated_resolution_and_checks_its_die() {
5849 const CURRENT_LSB: u32 = 1_000;
5852 assert_eq!(pamoja_ina226_calibration(CURRENT_LSB, 2), 2_560);
5853 assert_eq!(pamoja_ina226_minimum_current_lsb_microamps(15_000_000), 458);
5854 assert_eq!(pamoja_ina226_shunt_nanovolts(8_000), 20_000_000);
5855 assert_eq!(pamoja_ina226_bus_microvolts(9_584), 11_980_000);
5856 assert_eq!(
5857 pamoja_ina226_current_microamps(10_000, CURRENT_LSB),
5858 10_000_000
5859 );
5860 assert_eq!(
5862 pamoja_ina226_power_microwatts(4_792, CURRENT_LSB),
5863 119_800_000
5864 );
5865
5866 let mut die = PamojaIna226DieId {
5867 device: 0,
5868 revision: 0,
5869 };
5870 unsafe {
5872 assert_eq!(
5873 pamoja_ina226_identify(PAMOJA_INA226_MANUFACTURER_ID, 0x2260, &mut die),
5874 PamojaStatus::Ok
5875 );
5876 assert_eq!(die.device, PAMOJA_INA226_DEVICE_ID);
5877 assert_eq!(
5878 pamoja_ina226_identify(PAMOJA_INA226_MANUFACTURER_ID, 0x2270, &mut die),
5879 PamojaStatus::Codec,
5880 "a die that is not an INA226 is refused"
5881 );
5882 }
5883 }
5884}