1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
//!  Poll the temperature sensor every 10 seconds. Transmit the sensor data to the CoAP server after polling.
//!  This is the Rust version of https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/rust-nbiot/apps/my_sensor_app/OLDsrc/sensor.c

use mynewt::{
    result::*,                              //  Import Mynewt API Result and Error types
    hw::sensor::{        
        self,                               //  Import Mynewt Sensor API functions
        sensor_ptr,                         //  Import Mynewt Sensor API types
        sensor_arg, sensor_data_ptr, sensor_listener,
        sensor_temp_raw_data, sensor_type_t,
        SensorValue, SensorValueType,
    },
    sys::console,                           //  Import Mynewt Console API
    fill_zero, Strn,                        //  Import Mynewt macros    
};
use mynewt_macros::{ init_strn };           //  Import Mynewt procedural macros
use crate::app_network::send_sensor_data;   //  Import `app_network.rs` for sending sensor data

///  Sensor to be polled: `temp_stm32_0` is Blue Pill's internal temperature sensor
static SENSOR_DEVICE: Strn      = init_strn!("temp_stm32_0");
///  Poll sensor every 10,000 milliseconds (10 seconds)  
const SENSOR_POLL_TIME: u32     = (10 * 1000);  
///  Use key (field name) `t` to transmit raw temperature to CoAP Server
const TEMP_SENSOR_KEY: &str     = "t";
///  Type of sensor: Raw temperature sensor (integer sensor values 0 to 4095)
const TEMP_SENSOR_TYPE: sensor_type_t = sensor::SENSOR_TYPE_AMBIENT_TEMPERATURE_RAW;

///  Ask Mynewt to poll the temperature sensor every 10 seconds and call `handle_sensor_data()`.
///  Return `Ok()` if successful, else return `Err()` with `MynewtError` error code inside.
pub fn start_sensor_listener() -> MynewtResult<()>  {  //  Returns an error code upon error.
    console::print("Rust TMP poll\n");

    //  Set the sensor polling time to 10 seconds.  SENSOR_DEVICE is "temp_stm32_0", SENSOR_POLL_TIME is 10,000.
    sensor::set_poll_rate_ms(&SENSOR_DEVICE, SENSOR_POLL_TIME) ? ;

    //  Fetch the sensor by name, without locking the driver for exclusive access.
    let sensor = sensor::mgr_find_next_bydevname(&SENSOR_DEVICE, core::ptr::null_mut()) ? ;
    assert!(!sensor.is_null(), "no sensor");

    //  Define the listener function to be called after polling the temperature sensor.
    let listener = sensor_listener {
        sl_sensor_type: TEMP_SENSOR_TYPE,       //  Type of sensor: ambient temperature
        sl_func       : sensor::as_untyped(handle_sensor_data),  //  Listener function
        ..fill_zero!(sensor_listener)           //  Set other fields to 0
    };

    //  Register the Listener Function to be called every 10 seconds, with the polled sensor data.
    sensor::register_listener(sensor, listener) ? ;  //  `?` means in case of error, return error now.

    //  Return `Ok()` to indicate success.  This line should not end with a semicolon (;).
    Ok(())
}

///  This listener function is called every 10 seconds by Mynewt to handle the polled sensor data.
///  Return 0 if we have handled the sensor data successfully.
extern fn handle_sensor_data(sensor: sensor_ptr, _arg: sensor_arg, 
    sensor_data: sensor_data_ptr, sensor_type: sensor_type_t) -> MynewtError {
    console::print("Rust handle_sensor_data\n");

    //  Check that the temperature data is valid.
    if sensor_data.is_null() { return MynewtError::SYS_EINVAL; }  //  Exit if data is missing
    assert!(!sensor.is_null(), "null sensor");

    //  Get the temperature sensor value. It could be raw or computed.
    let sensor_value = convert_sensor_data(sensor_data, sensor_type);
    if let SensorValueType::None = sensor_value.val { assert!(false, "bad type"); }

    //  Compose a CoAP message with the temperature sensor data and send to the 
    //  CoAP server.  The message will be enqueued for transmission by the OIC 
    //  background task so this function will return without waiting for the message 
    //  to be transmitted.
    let res = send_sensor_data(&sensor_value);

    //  `SYS_EAGAIN` means that the Network Task is still starting up the network.
    //  We drop the sensor data and send at the next poll.
    if let Err(err) = res {  //  `if let` will assign `err` to the error code inside `res`
        if err == MynewtError::SYS_EAGAIN {
            console::print("TMP network not ready\n");
            return MynewtError::SYS_EOK; 
        }            
    }
    //  Return 0 to Mynewt to indicate no error.  Should not end with a semicolon (;).
    MynewtError::SYS_EOK
}

///  Convert the raw temperature value received from Mynewt into a `SensorValue` for transmission, which includes the sensor data key `t`. 
///  `sensor_type` indicates the type of data in `sensor_data`.
#[allow(non_snake_case, unused_variables)]
fn convert_sensor_data(sensor_data: sensor_data_ptr, sensor_type: sensor_type_t) -> SensorValue {
    console::print("TMP listener got rawtmp\n");
    //  Construct and return a new `SensorValue` (without semicolon)
    SensorValue {
        key: TEMP_SENSOR_KEY,  //  Sensor data key is `t`
        val: match sensor_type {
            SENSOR_TYPE_AMBIENT_TEMPERATURE_RAW => {  //  If this is raw temperature...
                //  Interpret the sensor data as a `sensor_temp_raw_data` struct that contains raw temp.
                let mut rawtempdata = fill_zero!(sensor_temp_raw_data);
                let rc = unsafe { sensor::get_temp_raw_data(sensor_data, &mut rawtempdata) };
                assert_eq!(rc, 0, "rawtmp fail");
                //  Check that the raw temperature data is valid.
                assert_ne!(rawtempdata.strd_temp_raw_is_valid, 0, "bad rawtmp");                
                //  Raw temperature data is valid.  Return it.
                SensorValueType::Uint(rawtempdata.strd_temp_raw)  //  Raw Temperature in integer (0 to 4095)
            }
            //  Unknown type of sensor value
            //  _ => { assert!(false, "sensor type"); SensorValueType::Uint(0) }
        }
    }
}