Various changes..

This commit is contained in:
FliegendeWurst 2021-09-08 15:42:33 +02:00
parent 98acedca0e
commit a24f9d86f1
5 changed files with 498 additions and 20 deletions

151
Cargo.lock generated
View File

@ -2,6 +2,17 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "autocfg"
version = "1.0.1"
@ -50,6 +61,21 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "dht-hal"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb5423e6132fe67189d1aeac8a7412e7d09816a7374e22201a49a2d5081c8549"
dependencies = [
"embedded-hal",
]
[[package]]
name = "display-interface"
version = "0.4.1"
@ -110,6 +136,18 @@ dependencies = [
"void",
]
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "float-cmp"
version = "0.8.0"
@ -129,6 +167,46 @@ dependencies = [
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if 1.0.0",
"libc",
"wasi",
]
[[package]]
name = "gpio-cdev"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da59d440ef8c26208b3960ed58b71b896db7dfd6bb138622bba733b66d978c3a"
dependencies = [
"bitflags",
"libc",
"nix",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash",
]
[[package]]
name = "hashlink"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
dependencies = [
"hashbrown",
]
[[package]]
name = "i2cdev"
version = "0.4.4"
@ -152,9 +230,20 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.65"
version = "0.2.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8"
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
[[package]]
name = "libsqlite3-sys"
version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290b64917f8b0cb885d9de0f9959fe1f775d7fa12f1da2db9001c1c8ab60f89d"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "linux-embedded-hal"
@ -178,6 +267,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8197ad28c61566fb74b2167563865906215fbc32c61fd866bd54e7c195eb84ed"
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "micromath"
version = "1.1.1"
@ -207,7 +302,7 @@ checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
dependencies = [
"bitflags",
"cc",
"cfg-if",
"cfg-if 0.1.10",
"libc",
"void",
]
@ -221,16 +316,48 @@ dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]]
name = "pkg-config"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]]
name = "raspi-oled"
version = "0.1.0"
dependencies = [
"dht-hal",
"embedded-graphics",
"embedded-hal",
"gpio-cdev",
"libc",
"linux-embedded-hal",
"machine-ip",
"rusqlite",
"ssd1306",
]
[[package]]
name = "rusqlite"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57adcf67c8faaf96f3248c2a7b419a0dbc52ebe36ba83dd57fe83827c1ea4eb3"
dependencies = [
"bitflags",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"memchr",
"smallvec",
]
[[package]]
name = "serial-core"
version = "0.4.0"
@ -252,6 +379,12 @@ dependencies = [
"termios",
]
[[package]]
name = "smallvec"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]]
name = "spidev"
version = "0.4.1"
@ -301,6 +434,12 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.3"
@ -312,3 +451,9 @@ name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"

View File

@ -9,8 +9,13 @@ edition = "2018"
[dependencies]
embedded-graphics = "0.7.1"
linux-embedded-hal = "0.3.0"
embedded-hal = "0.2.5"
machine-ip = "0.2.1"
ssd1306 = "0.6.0"
libc = "0.2.98"
gpio-cdev = "0.4"
dht-hal = "0.0.1"
rusqlite = { version = "0.25.3", features = ["bundled"] }
[profile.release]
codegen-units = 1

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2022 Arne Keller
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

140
src/lib.rs Normal file
View File

@ -0,0 +1,140 @@
use std::{thread::sleep, time::{self, Duration}};
use gpio_cdev::{EventType, Line, LineRequestFlags};
fn read_events(line: &gpio_cdev::Line, timeout: std::time::Duration) -> Result<Vec<(u64, EventType)>, SensorError> {
let input = line.request(
LineRequestFlags::INPUT,
0,
"read-data")?;
let mut last_state = 1;
let start = time::Instant::now();
let mut events = Vec::with_capacity(81);
while start.elapsed() < timeout && events.len() < 81 {
let new_state = input.get_value()?;
if new_state != last_state {
let timestamp = start.elapsed();
let event_type = if last_state < new_state {
EventType::RisingEdge
} else {
EventType::FallingEdge
};
events.push((timestamp.as_micros() as u64, event_type));
last_state = new_state;
}
}
if events.len() < 81 {
return Err(SensorError::Timeout);
}
Ok(events)
}
fn events_to_data(events: Vec<(u64, EventType)>) -> Vec<u8> {
events[1..]
.windows(2)
.map(|pair| {
let prev = pair.get(0).unwrap();
let next = pair.get(1).unwrap();
match next.1 {
EventType::FallingEdge => Some(next.0 - prev.0),
EventType::RisingEdge => None,
}
})
.filter(|&d| d.is_some())
.map(|elapsed| {
if elapsed.unwrap() > 35 { 1 } else { 0 }
}).collect()
}
const MAX_HUMIDITY: u16 = 1000;
fn process_data(mut bits: &[u8]) -> Result<(u16, u16), SensorError> {
if bits[0] == 1 {
// definitely incorrect first bit
// (the humidity can't be this big..)
bits = &bits[1..];
}
let bytes: Vec<u8> = bits
.chunks(8)
.map(|chunk| chunk.iter()
.enumerate()
// 8 bits, starting with the MSB
.map(|(bit_idx, &x)| x << (7 - bit_idx))
.sum()
).collect();
let rh = (bytes[0] as u16) << 8 | bytes[1] as u16;
if rh > MAX_HUMIDITY {
return Err(SensorError::HumidityTooHigh);
}
let celsius = (bytes[2] as u16) << 8 | bytes[3] as u16;
if bits.len() >= 40 {
let cksum: u8 = bits[32..40]
.iter()
.enumerate()
.map(|(idx, &x)| x << (7 - idx))
.sum();
let actual_sum = (bytes[0].wrapping_add(bytes[1]).wrapping_add(bytes[2]).wrapping_add(bytes[3])) & 0xff;
if actual_sum != cksum {
return Err(SensorError::ChecksumMismatch);
}
}
Ok((rh, celsius))
}
#[test]
fn test_process_data() {
let x = process_data(&[1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1]).unwrap();
assert_eq!(471, x.0);
assert_eq!(268, x.1);
}
#[derive(Debug)]
pub enum SensorError {
Io(gpio_cdev::Error),
ChecksumMismatch,
HumidityTooHigh,
Timeout
}
impl From<gpio_cdev::Error> for SensorError {
fn from(error: gpio_cdev::Error) -> Self {
SensorError::Io(error)
}
}
pub fn am2302_reading(line: &Line) -> Result<(u16, u16), SensorError> {
line.request(LineRequestFlags::OUTPUT, 1, "rust-am2302").unwrap();
sleep(Duration::from_millis(500));
set_max_priority();
// set low for 20 ms
if let Err(e) = line.request(LineRequestFlags::OUTPUT, 0, "rust-am2302") {
set_normal_priority();
return Err(SensorError::Io(e));
}
sleep(Duration::from_millis(3));
let events = read_events(&line, Duration::from_secs(1));
println!("{:?} {:?}", events, events.as_ref().map(|x| x.len()));
set_normal_priority();
let events = events?;
let data = events_to_data(events);
process_data(&data)
}
fn set_max_priority() {
unsafe {
let mut sched_para: libc::sched_param = std::mem::transmute([0u8; std::mem::size_of::<libc::sched_param>()]);
sched_para.sched_priority = libc::sched_get_priority_max(libc::SCHED_FIFO);
libc::sched_setscheduler(0, libc::SCHED_FIFO, (&sched_para) as *const libc::sched_param);
}
}
fn set_normal_priority() {
unsafe {
let sched_para: libc::sched_param = std::mem::transmute([0u8; std::mem::size_of::<libc::sched_param>()]);
libc::sched_setscheduler(0, libc::SCHED_OTHER, (&sched_para) as *const libc::sched_param);
}
}

View File

@ -1,4 +1,6 @@
use dht_hal::{Dht22, Reading};
use embedded_graphics::image::{Image, ImageRaw};
use embedded_graphics::mono_font::iso_8859_7::FONT_9X18;
use embedded_graphics::pixelcolor::BinaryColor;
use embedded_graphics::prelude::*;
use embedded_graphics::primitives::{Circle, Line, PrimitiveStyle, Rectangle};
@ -7,17 +9,144 @@ use embedded_graphics::{
mono_font::{ascii::FONT_6X10, MonoTextStyleBuilder},
text::Text,
};
use embedded_hal::digital::v2::{InputPin, OutputPin};
use embedded_hal::prelude::_embedded_hal_blocking_i2c_Write;
use gpio_cdev::{Chip, EventRequestFlags, EventType, LineRequestFlags};
use linux_embedded_hal::i2cdev::core::I2CDevice;
use linux_embedded_hal::i2cdev::linux::LinuxI2CError;
use rusqlite::{Connection, params};
use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306};
use linux_embedded_hal::I2cdev;
use linux_embedded_hal::{Delay, I2cdev};
use machine_ip;
use std::intrinsics::transmute;
use std::{env, mem, time};
use std::thread::sleep;
use std::time::Duration;
use std::time::{Duration, SystemTime};
static IMG_DATA: &[u8; 512] = include_bytes!("../rust.raw");
fn main() {
let i2c = I2cdev::new("/dev/i2c-1").unwrap();
const CCS811_ADDR: u8 = 0x5A; // or 0x5B
const CCS811_STATUS: u8 = 0x00;
const CCS811_MEAS_MODE: u8 = 0x01;
const CCS811_ALG_RESULT_DATA: u8 = 0x02;
const CCS811_RAW_DATA: u8 = 0x03;
const CCS811_ENV_DATA: u8 = 0x05;
const CCS811_NTC: u8 = 0x06;
const CCS811_THRESHOLDS: u8 = 0x10;
const CCS811_BASELINE: u8 = 0x11;
const CCS811_HW_ID: u8 = 0x20;
const CCS811_HW_VERSION: u8 = 0x21;
const CCS811_FW_BOOT_VERSION: u8 = 0x23;
const CCS811_FW_APP_VERSION: u8 = 0x24;
const CCS811_ERROR_ID: u8 = 0xE0;
const CCS811_APP_START: u8 = 0xF4;
const CCS811_SW_RESET: u8 = 0xFF;
struct CCS811 {
i2c: I2cdev,
addr: u8
}
impl CCS811 {
fn new(mut i2c: I2cdev, addr: u8) -> Self {
i2c.set_slave_address(addr as u16).unwrap();
Self {
i2c,
addr
}
}
fn check_for_error(&mut self) -> Option<u8> {
let x = self.i2c.smbus_read_byte_data(CCS811_STATUS).unwrap();
if (x & 1) != 0 {
let err_code = self.i2c.smbus_read_byte_data(CCS811_ERROR_ID).unwrap();
Some(err_code)
} else {
None
}
}
fn hardware_id(&mut self) -> u8 {
self.i2c.smbus_read_byte_data(CCS811_HW_ID).unwrap()
}
fn app_valid(&mut self) -> bool {
let x = self.i2c.smbus_read_byte_data(CCS811_STATUS).unwrap();
x & 1 << 4 != 0
}
fn set_drive_mode(&mut self, mode: CCS811DriveMode) -> Result<(), Option<LinuxI2CError>> {
self.i2c.smbus_write_byte(CCS811_APP_START).map_err(Some)?;
if let Some(x) = self.check_for_error() {
println!("error ignored {:b}", x);
}
let mut setting = self.i2c.smbus_read_byte_data(CCS811_MEAS_MODE).map_err(Some)?;
setting &= !(0b00000111 << 4);
setting |= (mode as u8) << 4;
self.i2c.smbus_write_byte_data(CCS811_MEAS_MODE, setting).map_err(Some)?;
Ok(())
}
fn get_baseline(&mut self) -> u16 {
let x = self.i2c.smbus_read_i2c_block_data(CCS811_BASELINE, 2).unwrap();
((x[0] as u16) << 8) | (x[1] as u16)
}
/// Returns (eCO2, tVOC)
fn get_reading(&mut self) -> (u16, u16) {
let x = self.i2c.smbus_read_i2c_block_data(CCS811_ALG_RESULT_DATA, 4).unwrap();
(((x[0] as u16) << 8) | (x[1] as u16), ((x[2] as u16) << 8) | (x[3] as u16))
}
}
enum CCS811DriveMode {
Idle = 0,
EverySecond = 1,
Every10Seconds = 2,
Every60Seconds = 3,
/// Note the English manual states this is calculated every 10 ms!
Every250Milliseconds = 4,
}
fn main() {
let args = env::args().collect::<Vec<_>>();
if args.len() < 2 {
panic!("missing argument: database path");
}
let database = Connection::open(&args[1]).expect("failed to open database");
database.execute("
CREATE TABLE IF NOT EXISTS sensor_readings(
time INTEGER PRIMARY KEY,
humidity INTEGER NOT NULL,
celsius INTEGER NOT NULL
)", []).unwrap();
/*
let mut ccs = CCS811::new(i2c, CCS811_ADDR);
println!("HW ID, should be 0x81 {:x}", ccs.hardware_id());
println!("Error code, should be None: {:?}", ccs.check_for_error());
println!("app valid = {:?}", ccs.app_valid());
println!("baseline = {:x}", ccs.get_baseline());
println!("reading {:?}", ccs.get_reading());
println!("setting drive mode to 1: {:?}", ccs.set_drive_mode(CCS811DriveMode::EverySecond));
*/
let mut chip = Chip::new("/dev/gpiochip0").unwrap();
let line = chip.get_line(26).unwrap();
for _attempt in 0..5 {
let time = std::time::SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
if let Ok((rh, temp)) = raspi_oled::am2302_reading(&line) {
database.execute("INSERT INTO sensor_readings (time, humidity, celsius) VALUES (?1, ?2, ?3)", params![time, rh, temp]).unwrap();
display_on_ssd1306(rh, temp);
break;
}
}
}
fn display_on_ssd1306(rh: u16, temp: u16) {
let i2c = I2cdev::new("/dev/i2c-1").unwrap();
let interface = I2CDisplayInterface::new(i2c);
let mut disp = Ssd1306::new(
interface,
@ -26,53 +155,63 @@ fn main() {
).into_buffered_graphics_mode();
disp.init().unwrap();
disp.flush().unwrap();
let text_style = MonoTextStyleBuilder::new()
.font(&FONT_6X10)
.font(&FONT_9X18)
.text_color(BinaryColor::On)
.build();
let text = "0123456789012345678901";
let text = format!("{}.{}% {}.{}°C", rh / 10, rh % 10, temp / 10, temp % 10);
Text::new(&text, Point::new(0, 10), text_style)
.draw(&mut disp)
.unwrap();
disp.flush().unwrap();
/*
sleep(Duration::from_secs(2));
disp.clear();
let base_y = 0.0;
let max_dy = 32.0;
let mut tick = 0;
loop {
Line::new(Point::new(8, 16 + 16), Point::new(8 + 16, 16 + 16))
let y = if tick % 32 < 16 {
base_y + (tick % 16) as f32 / 16.0 * max_dy
} else {
base_y + max_dy - (tick % 16) as f32 / 16.0 * max_dy
} as i32;
tick += 1;
Line::new(Point::new(8, y + 16), Point::new(8 + 16, y + 16))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
.draw(&mut disp).unwrap();
Line::new(Point::new(8, 16 + 16), Point::new(8 + 8, 16))
Line::new(Point::new(8, y + 16), Point::new(8 + 8, y))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
.draw(&mut disp).unwrap();
Line::new(Point::new(8 + 16, 16 + 16), Point::new(8 + 8, 16))
Line::new(Point::new(8 + 16, y + 16), Point::new(8 + 8, y))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
.draw(&mut disp).unwrap();
Rectangle::new(Point::new(48, 16), Size::new(16, 16))
Rectangle::new(Point::new(48, y), Size::new(16, 16))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
.draw(&mut disp).unwrap();
Circle::new(Point::new(88, 16), 17)
Circle::new(Point::new(88, y), 16)
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
.draw(&mut disp).unwrap();
let local_addr = machine_ip::get().unwrap();
Text::new(&format!("IP: {}", local_addr.to_string()), Point::new(0, 56), text_style)
/*
Text::new(&format!("Hello from frame {}", tick), Point::new(0, 56), text_style)
.draw(&mut disp)
.unwrap();
*/
disp.flush().unwrap();
sleep(Duration::from_secs(2));
sleep(Duration::from_millis(10));
disp.clear();
/*
let im: ImageRaw<BinaryColor> = ImageRaw::new(IMG_DATA, 64);
let img = Image::new(&im, Point::new(32, 0));
img.draw(&mut disp).unwrap();
@ -80,5 +219,35 @@ fn main() {
sleep(Duration::from_secs(2));
disp.clear();
*/
}
*/
}
struct LineWrapper(gpio_cdev::Line);
impl InputPin for LineWrapper {
type Error = gpio_cdev::Error;
fn is_high(&self) -> Result<bool, Self::Error> {
let handle = self.0.request(LineRequestFlags::INPUT, 0, "rust-line-wrapper")?;
Ok(handle.get_value()? == 1)
}
fn is_low(&self) -> Result<bool, Self::Error> {
let handle = self.0.request(LineRequestFlags::INPUT, 0, "rust-line-wrapper")?;
Ok(handle.get_value()? == 0)
}
}
impl OutputPin for LineWrapper {
type Error = gpio_cdev::Error;
fn set_low(&mut self) -> Result<(), Self::Error> {
self.0.request(LineRequestFlags::OUTPUT, 1, "rust-line-wrapper")?.set_value(0)
}
fn set_high(&mut self) -> Result<(), Self::Error> {
self.0.request(LineRequestFlags::OUTPUT, 1, "rust-line-wrapper")?.set_value(1)
}
}