Closed pdgilbert closed 3 years ago
hey thanks for the questions! all of this should work (and we've been using it under linux for a couple of years now), but, there's also plenty of room for improvement and i have a bunch of updates that i haven't had a chance to make just yet, so, you might experience a bit of flux while I do this (sorry).
Should this crate work with no_std (I hope so)? There seems to be some indirect dependency on ansi_term, lazy_static and termcolor which I think need std so I get an error about can't find crate std. I am using master branch.
yep, it should! though it's possible i have made a mistake in there. for no_std
it's always worth checking Cargo.toml to see what features are available, if you specify default-features=false
it should drop all std
requirements.
With no luck finding an example, I've looked at the integration test, but it uses std. Is there another example I should be looking at?
the utility is probably the best place to look, again this runs on linux so it's not quite no_std
but the fundamentals are all the same.
Should the crate already work if I only have one radio, so do not have the problem described in issue #10 ?
yep, even multiple radios should be okay provided there's no multithreading.
I think the frequency is a u32 indicating hertz but somewhere I saw something that that seemed like it might be megahertz. Megahertz would mean only some channels can be used (867.0, 868.0, 915.0). Am I correct that the frequency in hertz?
it should be in Hz yes, you can see this in the docs
The integration test loads a configuration from a file. Thinking no_std I would like to just specify the configuration in the code, but I cannot figure out if I should be setting this with radio_sx127x::spi(), Sx127x::new(), or with lora_configure() after I have an object. I am confused about what is intended to be the user API and what are internal utilities. A quick reaction to my attempt (code below) would be much appreciated.
::spi()
is used to construct an SPI based instance (as opposed to UART which i haven't yet implemented) so, that's the right one, and Config::default()
should get you most of the way, which you can modify as required
I am also very unsure about the hardware wiring, so I have added comments in the code. From #7 I see that "busy" should DIO0, I guess on a gpio pin configured as push_pull_output? I am also unsure about the pin for "ready". Should it be another of the DIOx pins?
BUSY
and READY
need to be configured as input pins for the radio to indicate state to the micro. I don't have time to look at the code at the moment sorry but, hopefully that's enough to get you started!
Thank you for the quick response. The default-features=false
was the key to remove the need forstd
, and the other hints have been very useful. I have resolved many things.
Also thank you for warning about "experience a bit of flux". I fully anticipate that. I hope when I get my examples working they might be helpful to you while you make changes.
I can setup LoRaChannel{}
which forms part of radio_sx127x::device::Config{}
, and then use that in Sx127x::spi()
to get the object I use to transmit. And I can build the structure LoRaConfig{}
, but it never gets used. The code now builds but since part of the configuration is not used I don't think it can work properly. (I had to switch from blue pill to stm32f411 because the code was too large to load.)
There is an associated function for the Sx127x::spi()
object that looks like it might be able to set the LoRaConfig
but it is not public:
lora.lora_configure( config_lora, &config_ch ).unwrap();
| ^^^^^^^^^^^^^^ private associated function
Also, the associated function lora_get_config()
is not public and it would be useful for debugging.
The code is below for reference.
If you know which DIOx connection should be connected for ReadyPin
I would appreciate hearing.
Finally, I find I need use radio::Transmit;
because the trait needs to be in scope to find methods start_transmit
and check_transmit
. Could this (and Receive) not be re-exported from radio_sx127x
so the user does not need to use crate radio
?
Thanks again.
#![no_std]
#![no_main]
#[cfg(debug_assertions)]
extern crate panic_semihosting;
#[cfg(not(debug_assertions))]
extern crate panic_halt;
// use nb::block;
use cortex_m_rt::entry;
use cortex_m_semihosting::*;
//use asm_delay::{ AsmDelay, bitrate, };
extern crate radio_sx127x;
use radio_sx127x::{prelude::*,
device::{Modem, Channel, PaConfig, },
device::lora::{LoRaConfig, LoRaChannel, Bandwidth, SpreadingFactor, CodingRate,
PayloadLength, PayloadCrc, FrequencyHopping, },
};
extern crate radio;
//use radio::{Receive, Transmit};
use radio::{Transmit}; // trait needs to be in scope to find methods start_transmit and check_transmit.
use stm32f4xx_hal::{prelude::*,
pac::Peripherals,
spi::{Spi},
delay::Delay,
time::MegaHertz,
};
const FREQUENCY: u32 = 915_000_000; // frequency in hertz
#[entry]
fn main() -> !{
// base setup ( using stm32f4xx_hal )
let cp = cortex_m::Peripherals::take().unwrap();
let p = Peripherals::take().unwrap();
let rcc = p.RCC.constrain();
let clocks = rcc.cfgr.sysclk(64.mhz()).pclk1(32.mhz()).freeze();
let gpioa = p.GPIOA.split();
let gpiob = p.GPIOB.split();
let spi = Spi::spi1(
p.SPI1,
(gpioa.pa5.into_alternate_af5(), // sck on PA5
gpioa.pa6.into_alternate_af5(), // miso on PA6
gpioa.pa7.into_alternate_af5() // mosi on PA7
),
sx127x_lora::MODE,
MegaHertz(8).into(),
clocks,
);
let delay = Delay::new(cp.SYST, clocks);
// Create lora radio instance
let config_ch = LoRaChannel {
freq: FREQUENCY as u32, // frequency in hertz
bw: Bandwidth::Bw125kHz,
sf: SpreadingFactor::Sf7,
cr: CodingRate::Cr4_8,
};
// let config_radio = Config::default() ;
let config_radio = radio_sx127x::device::Config {
modem: Modem::LoRa(LoRaConfig::default()),
channel: Channel::LoRa(config_ch),
pa_config: PaConfig::default(),
xtal_freq: 32000000,
timeout_ms: 100,
};
let config_lora = LoRaConfig {
preamble_len: 0x8,
symbol_timeout: 0x64,
payload_len: PayloadLength::Variable,
payload_crc: PayloadCrc::Enabled,
frequency_hop: FrequencyHopping::Disabled,
invert_iq: false,
};
// other settings?
// lora.set_mode(sx127x_lora::RadioMode::Stdby).unwrap();
//baud = 1000000 is this needed for spi or just USART
// open_drain_output is really input and output. BusyPin is just input, but I think this should work
// gpiob.pb8.into_alternate_open_drain(&mut gpiob.crh),
// however, gives trait bound ... InputPin` is not satisfied
let mut lora = Sx127x::spi(
spi, //Spi,
gpioa.pa1.into_push_pull_output(), //CsPin, on PA1
gpiob.pb8.into_floating_input(), //BusyPin, D00 on PB8
gpiob.pb9.into_floating_input(), //ReadyPin, D01 ? on PB9
gpioa.pa0.into_push_pull_output(), //ResetPin, on PA0
delay, //Delay,
&config_radio, //&Config,
).unwrap(); // should handle error
//DIO0 triggers RxDone/TxDone status.
//DIO1 triggers RxTimeout and other errors status.
//D02, D03 ?
//lora.lora_configure( config_lora, &config_ch ).unwrap();
// print out configuration (for debugging)
// let v = lora.lora_get_config();
// hprintln!("configuration {}", v).unwrap();
// hprintln!("channel {}", lora.get_channel()).unwrap();
// transmit something
//let buffer = &[0xaa, 0xbb, 0xcc];
let message = "Hello, LoRa!";
let mut buffer = [0;255];
for (i,c) in message.chars().enumerate() {
buffer[i] = c as u8;
}
lora.start_transmit(&buffer).unwrap(); // should handle error
if lora.check_transmit().unwrap() {
hprintln!("TX complete").unwrap();
};
loop {};
}
I can setup LoRaChannel{} which forms part of radio_sx127x::device::Config{}, and then use that in Sx127x::spi() to get the object I use to transmit. And I can build the structure LoRaConfig{}, but it never gets used
you're on the right path. LoRaConfig
goes in Config::Modem(Modem::Lora(..))
, same as you've done with LoRaChannel
.
(I had to switch from blue pill to stm32f411 because the code was too large to load.)
are you building in release mode? this makes a huge difference, though rust builds can be kindof large. you may find useful guidance here or here, and it's worth noting that any print
functions or panics
(ie. anywhere you have .unwrap()
) can add rather a lot of overhead to the binary. there's also cargo-bloat for investigating this but i haven't used it heavily.
If you know which DIOx connection should be connected for ReadyPin I would appreciate hearing.
Busy is DIO0, Ready is DIO1 (and these should be at some point renamed...)
Could this (and Receive) not be re-exported from radio_sx127x so the user does not need to use crate radio?
Probably could be from the prelude yeah, the radio
crate is somewhat likely to be required anyway though.
Thank you for all your help. I have now tested with stm32f411
on 900Mhz band channels 12 and 02 (915_000_000
hz and 907_400_000
hz) and can receive transmission with my base station python code on R Pi. That is to say, it not only compiles but also works. For reference my code is below, but I do realize your crate is in flux and will probably change soon.
Thank you for the pointer about building in release mode for bluepill. Yes, I can fit the code on bluepill this way. I have not yet done a test to see if it actually works, but will report back when I do.
A remaining question is regarding using a borrowed copy of delay
. With some other crates I have been able to pass &delay
in the setup, but I have not been able to do that with Sx127x::spi
. This means I had to set up a second delay function to slow down my loop. Is there another way to do this?
#![no_std]
#![no_main]
#[cfg(debug_assertions)]
extern crate panic_semihosting;
#[cfg(not(debug_assertions))]
extern crate panic_halt;
use cortex_m_rt::entry;
use cortex_m_semihosting::*;
use asm_delay::{ AsmDelay, bitrate, };
extern crate radio_sx127x;
use radio_sx127x::{prelude::*,
device::{Modem, Channel, PaConfig, },
device::lora::{LoRaConfig, LoRaChannel, Bandwidth, SpreadingFactor, CodingRate,
PayloadLength, PayloadCrc, FrequencyHopping, },
};
extern crate radio;
use radio::{Transmit}; // trait needs to be in scope to find methods start_transmit and check_transmit.
use stm32f4xx_hal::{prelude::*,
pac::Peripherals,
spi::{Spi},
delay::Delay,
time::MegaHertz,
};
const FREQUENCY: u32 = 907_400_000; // frequency in hertz ch_12: 915_000_000, ch_2: 907_400_000
#[entry]
fn main() -> !{
// base setup ( using stm32f4xx_hal )
let cp = cortex_m::Peripherals::take().unwrap();
let p = Peripherals::take().unwrap();
let rcc = p.RCC.constrain();
let clocks = rcc.cfgr.sysclk(64.mhz()).pclk1(32.mhz()).freeze();
let gpioa = p.GPIOA.split();
let gpiob = p.GPIOB.split();
let spi = Spi::spi1(
p.SPI1,
(gpioa.pa5.into_alternate_af5(), // sck on PA5
gpioa.pa6.into_alternate_af5(), // miso on PA6
gpioa.pa7.into_alternate_af5() // mosi on PA7
),
sx127x_lora::MODE,
MegaHertz(8).into(),
clocks,
);
let delay = Delay::new(cp.SYST, clocks);
let mut delay2 = AsmDelay::new(bitrate::U32BitrateExt::mhz(32));
// Create lora radio instance
let config_ch = LoRaChannel {
freq: FREQUENCY as u32, // frequency in hertz
bw: Bandwidth::Bw125kHz,
sf: SpreadingFactor::Sf7,
cr: CodingRate::Cr4_8,
};
let config_lora = LoRaConfig {
preamble_len: 0x8,
symbol_timeout: 0x64,
payload_len: PayloadLength::Variable,
payload_crc: PayloadCrc::Enabled,
frequency_hop: FrequencyHopping::Disabled,
invert_iq: false,
};
//let config_radio = Config::default() ;
let config_radio = radio_sx127x::device::Config {
modem: Modem::LoRa(config_lora),
channel: Channel::LoRa(config_ch),
pa_config: PaConfig::default(),
xtal_freq: 32000000,
timeout_ms: 100,
};
let mut lora = Sx127x::spi(
spi, //Spi,
gpioa.pa1.into_push_pull_output(), //CsPin, on PA1
gpiob.pb8.into_floating_input(), //BusyPin, DIO0 on PB8
gpiob.pb9.into_floating_input(), //ReadyPin, DIO1 on PB9
gpioa.pa0.into_push_pull_output(), //ResetPin, on PA0
delay, //Delay,
&config_radio, //&Config,
).unwrap(); // should handle error
//hprintln!("lora object returned.").unwrap();
// print out configuration (for debugging)
// let v = lora.lora_get_config();
// hprintln!("configuration {}", v).unwrap();
// hprintln!("chammel {}", lora.get_chammel()).unwrap();
// transmit something
let message = b"Hello, LoRa!";
hprintln!("entering loop. ^C to quit.").unwrap();
loop {
lora.start_transmit(message).unwrap(); // should handle error
match lora.check_transmit() {
Ok(b) => if b {hprintln!("TX complete").unwrap()}
else {hprintln!("TX not complete").unwrap()},
Err(_err) => hprintln!("Error in lora.check_transmit(). Should return True or False.").unwrap(),
};
delay2.delay_ms(5000u32);
};
}
I have now tested with stm32f411 on 900Mhz band channels 12 and 02 (915_000_000 hz and 907_400_000hz) and can receive transmission with my base station python code on R Pi
wow v exciting! what's the plan now?
A remaining question is regarding using a borrowed copy of delay. With some other crates I have been able to pass &delay in the setup, but I have not been able to do that with Sx127x::spi. This means I had to set up a second delay function to slow down my loop. Is there another way to do this?
unfortunately DelayMs takes &mut self
so you do need to transfer ownership to keep the borrow checker happy... that said the Sx127x
instance re-implements this trait so you should be able to call lora.delay_ms(500u32);
?
I switched to using lora.delay_ms(5000u32);
and that works fine.
Regarding plans, I have revised this example so it works with multiple embedded stm hal crates and will shortly check those that I have hardware for. I am also working on an example that reads and transmits GPS and another to receive LoRa. I have a CI generated scoreboard for my examples at https://pdgilbert.github.io/eg_stm_hal/. (See lora_spi_*
examples in the second table.) There is still some cleaning up to do.
If you want to put these in an examples/
directory in your crate or rework them into tests please do not hesitate to use them. If you like I could fork your repository, addexamples/
and send a pull request.
I do have some other issues/questions but they are enough different that I think it is best to close this issue.
If you want to put these in an examples/ directory in your crate or rework them into tests please do not hesitate to use them. If you like I could fork your repository, add examples/ and send a pull request.
some examples would be super neat! i wonder whether there's a standard-ish board/combination that's easy to get hold of to reproduce / use these.
I am not aware of pre-build standard-ish boards with LoRa and GPS. So there is some wiring involved, but the pieces I am using are all pretty standard and cheap. (Roughly $5 each for the three pieces.) I am planning on documenting this more on my github site, but for an example in your crate I would put more comments in the files. The code should actually compile for many (most?) of the embedded-hal crates and, sometime, I will test with other GPS and LoRa. BTW, I just ran the GPS version of the code on battery powered blackpill stm32f411 without semihosting and it works.
I realize this is work in progress, so maybe you are not ready for newbies like me. Don't hesitate to tell me I need to be patient, but if your direction is not consistent with what I am trying to do then it would be nice to know that I should be looking elsewhere.
I am trying to build the LoRa sending side of a sensor to base station connection. I have both sides working on R Pi with python, but would like to run at least the sending side with rust on no_std stm32xxx. So I do have a working receiving side to test against. I am currently using an RFM95 sx1276 style radio.
Should this crate work with no_std (I hope so)? There seems to be some indirect dependency on
ansi_term
,lazy_static
andtermcolor
which I think need std so I get an error about can't find crate std. I am using master branch.With no luck finding an example, I've looked at the integration test, but it uses std. Is there another example I should be looking at?
Should the crate already work if I only have one radio, so do not have the problem described in issue #10 ?
I think the frequency is a u32 indicating hertz but somewhere I saw something that that seemed like it might be megahertz. Megahertz would mean only some channels can be used (867.0, 868.0, 915.0). Am I correct that the frequency in hertz?
The integration test loads a configuration from a file. Thinking no_std I would like to just specify the configuration in the code, but I cannot figure out if I should be setting this with
radio_sx127x::spi()
,Sx127x::new()
, or withlora_configure()
after I have an object. I am confused about what is intended to be the user API and what are internal utilities. A quick reaction to my attempt (code below) would be much appreciated.I am also very unsure about the hardware wiring, so I have added comments in the code. From #7 I see that "busy" should
DIO0
, I guess on a gpio pin configured aspush_pull_output
? I am also unsure about the pin for "ready". Should it be another of theDIOx
pins?Thanks very much for your work on this crate.