summaryrefslogtreecommitdiff
path: root/examples/nrf/src/bin/lora_p2p_sense.rs
blob: 3c6bb8767e7a35da0c13ae2472441890937e3285 (plain)
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio.
#![no_std]
#![no_main]
#![macro_use]
#![feature(type_alias_impl_trait)]
#![feature(alloc_error_handler)]
#![allow(incomplete_features)]

use defmt::*;
use embassy_executor::Spawner;
use embassy_lora::sx126x::*;
use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin as _, Pull};
use embassy_nrf::temp::Temp;
use embassy_nrf::{interrupt, spim};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::pubsub::{PubSubChannel, Publisher};
use embassy_time::{Duration, Timer};
use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig};
use {defmt_rtt as _, panic_probe as _, panic_probe as _};

// Sensor packet constants
const TEMPERATURE_UID: u8 = 0x01;
const MOTION_UID: u8 = 0x02;

// Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection)
static MESSAGE_BUS: PubSubChannel<CriticalSectionRawMutex, Message, 2, 1, 2> = PubSubChannel::new();

#[derive(Clone, defmt::Format)]
enum Message {
    Temperature(i32),
    MotionDetected,
}

#[embassy_executor::task]
async fn temperature_task(
    mut temperature: Temp<'static>,
    publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>,
) {
    Timer::after(Duration::from_secs(45)).await; // stabilize for 45 seconds

    let mut temperature_reporting_threshhold = 10;

    loop {
        let value = temperature.read().await;
        let mut temperature_val = value.to_num::<i32>();

        info!("Temperature: {}", temperature_val);

        // only report every 2 degree Celsius drops, from 9 through 5, but starting at 3 always report

        if temperature_val == 8 || temperature_val == 6 || temperature_val == 4 {
            temperature_val += 1;
        }

        if temperature_reporting_threshhold > temperature_val
            && (temperature_val == 9 || temperature_val == 7 || temperature_val == 5)
        {
            temperature_reporting_threshhold = temperature_val;
            publisher.publish(Message::Temperature(temperature_val)).await;
        } else if temperature_val <= 3 {
            publisher.publish(Message::Temperature(temperature_val)).await;
        }

        Timer::after(Duration::from_secs(20 * 60)).await;
    }
}

#[embassy_executor::task]
async fn motion_detection_task(
    mut pir_pin: Input<'static, AnyPin>,
    publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>,
) {
    Timer::after(Duration::from_secs(30)).await; // stabilize for 30 seconds

    loop {
        // wait for motion detection
        pir_pin.wait_for_low().await;
        publisher.publish(Message::MotionDetected).await;

        // wait a minute before setting up for more motion detection
        Timer::after(Duration::from_secs(60)).await;
    }
}

#[embassy_executor::main]
async fn main(spawner: Spawner) {
    let p = embassy_nrf::init(Default::default());
    // set up to funnel temperature and motion detection events to the Lora Tx task
    let mut lora_tx_subscriber = unwrap!(MESSAGE_BUS.subscriber());
    let temperature_publisher = unwrap!(MESSAGE_BUS.publisher());
    let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher());

    let mut spi_config = spim::Config::default();
    spi_config.frequency = spim::Frequency::M1; // M16 ???

    let mut radio = {
        let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
        let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config);

        let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard);
        let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard);
        let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
        let busy = Input::new(p.P1_14.degrade(), Pull::Down);
        let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard);
        let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard);

        match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await {
            Ok(r) => r,
            Err(err) => {
                info!("Sx126xRadio error = {}", err);
                return;
            }
        }
    };

    // set up for the temperature task
    let temperature_irq = interrupt::take!(TEMP);
    let temperature = Temp::new(p.TEMP, temperature_irq);

    // set the motion detection pin
    let pir_pin = Input::new(p.P0_10.degrade(), Pull::Up);

    let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
    let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);

    start_indicator.set_high();
    Timer::after(Duration::from_secs(5)).await;
    start_indicator.set_low();

    match radio.lora.sleep().await {
        Ok(()) => info!("Sleep successful"),
        Err(err) => info!("Sleep unsuccessful = {}", err),
    }

    unwrap!(spawner.spawn(temperature_task(temperature, temperature_publisher)));
    unwrap!(spawner.spawn(motion_detection_task(pir_pin, motion_detection_publisher)));

    loop {
        let message = lora_tx_subscriber.next_message_pure().await;

        let tx_config = TxConfig {
            // 11 byte maximum payload for Bandwidth 125 and SF 10
            pw: 20, // up to 20 // 5 ???
            rf: RfConfig {
                frequency: 903900000, // channel in Hz, not MHz
                bandwidth: Bandwidth::_250KHz,
                spreading_factor: SpreadingFactor::_10,
                coding_rate: CodingRate::_4_8,
            },
        };

        let mut buffer = [TEMPERATURE_UID, 0xffu8, MOTION_UID, 0x00u8];
        match message {
            Message::Temperature(temperature) => buffer[1] = temperature as u8,
            Message::MotionDetected => buffer[3] = 0x01u8,
        };

        // crypto for text ???
        match radio.tx(tx_config, &buffer).await {
            Ok(ret_val) => info!("TX ret_val = {}", ret_val),
            Err(err) => info!("TX error = {}", err),
        }

        match radio.lora.sleep().await {
            Ok(()) => info!("Sleep successful"),
            Err(err) => info!("Sleep unsuccessful = {}", err),
        }

        debug_indicator.set_high();
        Timer::after(Duration::from_secs(5)).await;
        debug_indicator.set_low();
    }
}