Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

no_std newbie questions #11

Closed
pdgilbert opened this issue Nov 8, 2020 · 8 comments
Closed

no_std newbie questions #11

pdgilbert opened this issue Nov 8, 2020 · 8 comments

Comments

@pdgilbert
Copy link

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 and termcolor 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 Question: Integration testing example #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 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.

  • I am also very unsure about the hardware wiring, so I have added comments in the code. From Question: SPI settings Raspberry Pi 3 B+ Lora HAT #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?

Thanks very much for your work on this crate.

#![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::*,
                   LoRaConfig,
                   LoRaChannel,
		   };

extern crate radio;
use radio::{Receive, Transmit};

use stm32f1xx_hal::{prelude::*,   
                    pac::Peripherals, 
                    spi::{Spi, Spi1NoRemap},
                    delay::Delay,
                    gpio::{gpioa::{PA5, PA6, PA7}, Alternate, Input, Floating,  
                           gpioa::{PA0, PA1}, Output, PushPull},
                    device::SPI1,
                    }; 


const FREQUENCY: u32 = 915_000_000;  // frequency in hertz


#[entry]
fn main() -> !{

    // base setup  ( using stm32f1xx_hal )
    
    let cp = cortex_m::Peripherals::take().unwrap();
    let p  = Peripherals::take().unwrap();
    
    let mut rcc   = p.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(64.mhz()).pclk1(32.mhz()).freeze(&mut p.FLASH.constrain().acr);
    
    let mut afio = p.AFIO.constrain(&mut rcc.apb2);
    let mut gpioa = p.GPIOA.split(&mut rcc.apb2);
    let mut gpiob = p.GPIOB.split(&mut rcc.apb2);

    // stm32f1xx_hal spi setup
    let spi = Spi::spi1(
    	p.SPI1,
    	(gpioa.pa5.into_alternate_push_pull(&mut gpioa.crl),  //   sck   on PA5
    	 gpioa.pa6.into_floating_input(&mut gpioa.crl),       //   miso  on PA6
    	 gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl)   //   mosi  on PA7
    	 ),
    	&mut afio.mapr,
    	sx127x_lora::MODE, 
    	8.mhz(),	
    	clocks, 
    	&mut rcc.apb2,
    	);
       
    let mut delay = Delay::new(cp.SYST, clocks);
 
    
    /// Create lora radio instance 
    
    let config = 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();
    //    set_tx_power(level, output_pin) level >17 => PA_BOOST. 
    //    lora.set_tx_power(17,1).unwrap();  
    //    lora.set_tx_power(15,1).unwrap();  
    
    let ch = LoRaChannel {
    		freq: FREQUENCY as u32, 	   // frequency in hertz
    		bw: Bandwidth::Bw125kHz,
    		sf: SpreadingFactor::Sf7,
    		cr: CodingRate::Cr4_8,  	  
    		}
    
    //baud = 1000000 is this needed for spi or just USART
    
          (gpiob.pb8.into_alternate_open_drain(&mut gpiob.crh),   // scl on PB8
           gpiob.pb9.into_alternate_open_drain(&mut gpiob.crh)),  // sda on PB9

    let mut lora = radio_sx127x::spi(
    	    spi,					       //Spi,
    	    gpioa.pa1.into_push_pull_output(&mut gpioa.crl),   //CsPin,   on PA1
    	    gpiob.pb8.into_push_pull_output(&mut gpiob.crh),   //BusyPin, D00 on PB8
    	    gpiob.pb9.into_push_pull_output(&mut gpiob.crh)),  ///ReadyPin, D01 ? on PB9
    	    gpioa.pa0.into_push_pull_output(&mut gpioa.crl),   //ResetPin, on PA0
    	    delay,					       //Delay,
    	    &config,					       //&Config,
    	    ).unwrap();      // should handle error
    
    //DIO0  triggers RxDone/TxDone status.
    //DIO1  triggers RxTimeout and other errors status.
    //D02, D03 ?
    
    // or ?
    //   pub fn new(hal: Base, config: &Config) -> Result<Self, Error<CommsError, PinError>> {
    //let mut lora = Sx127x::new( w2, &config ).unwrap();   // should handle error
    
    // or ?
    // lora.lora_configure( config, channel: &LoRaChannel, ).unwrap()
    
    
    
    // print out configuration (for debugging)
    
    let v = lora.lora_get_config():
    hprintln!("configuration {}", v).unwrap()
    
    hprintln!("chammel	  {}", lora.get_chammel()).unwrap();

    //hprintln!("mode		  {}", lora.get_mode()).unwrap();
    //hprintln!("mode		  {}", lora.read_register(Register::RegOpMode.addr())).unwrap();
    //hprintln!("bandwidth	  {:?}", lora.get_signal_bandwidth()).unwrap();
    //hprintln!("coding_rate	  {:?}",  lora.get_coding_rate_4()).unwrap();
    //hprintln!("spreading_factor {:?}",  lora.get_spreading_factor()).unwrap();
    //hprintln!("spreading_factor {:?}",  
    //hprintln!("invert_iq	  {:?}",  lora.get_invert_iq()).unwrap();
    //hprintln!("tx_power	  {:?}",  lora.get_tx_power()).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");
    		};
    
    
    
    loop {};
}

@ryankurte
Copy link
Member

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!

@pdgilbert
Copy link
Author

Thank you for the quick response. The default-features=false was the key to remove the need for std, 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 {};
}

@ryankurte
Copy link
Member

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.

@pdgilbert
Copy link
Author

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_000hz) 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);
       };
}

@ryankurte
Copy link
Member

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);?

@pdgilbert
Copy link
Author

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, add examples/ 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.

@ryankurte
Copy link
Member

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.

@pdgilbert
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants