A Rusty interface for the RN2903 LoRa module's serial protocol
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

117 lines
4.3KB

  1. // One of the critical aspects of this library is error handling. Because it is intended
  2. // to communicate with an external device, any operation could discover a disconnection
  3. // from the RN2903 serial link, so everything which does such communication will return
  4. // a `Result<T, rn2903::Error>`.
  5. #[macro_use] extern crate quick_error;
  6. use std::io;
  7. quick_error! {
  8. #[derive(Debug)]
  9. pub enum Error {
  10. /// The connection to the RN2903 was impossible for some reason. Perhaps an
  11. /// invalid port was specified, or this program does not have permission to
  12. /// access the specified port.
  13. ConnectionFailed(err: serialport::Error) {
  14. cause(err)
  15. description(err.description())
  16. from()
  17. }
  18. /// The device to which the serial link is connected does not appear to be
  19. /// a RN2903, because it did not respond to `sys get ver` correctly.
  20. WrongDevice(version: String) {
  21. description("failed to verify connected module")
  22. display("Could not verify version string. Expected a RN2903 firmware revision, got '{}'",
  23. version)
  24. }
  25. /// The program has become disconnected from the RN2903 module due to an I/O
  26. /// error. It is possible the device was physically disconnected, or that the
  27. /// host operating system closed the serial port for some reason.
  28. Disconnected(err: io::Error) {
  29. cause(err)
  30. description(err.description())
  31. from()
  32. }
  33. }
  34. }
  35. /// Universal `Result` wrapper for the RN2903 interface.
  36. type Result<T> = std::result::Result<T, Error>;
  37. // It's first necessary to actually connect to the module. To this end, the library
  38. // exports all the configuration information needed to configure a serial port to
  39. // communicate correctly with an RN2903.
  40. use serialport::prelude::*;
  41. use std::io::prelude::*;
  42. use core::convert::AsRef;
  43. use std::ffi::OsStr;
  44. use std::thread;
  45. use core::time::Duration;
  46. /// Returns a `serialport::SerialPortSettings` corresponding to the default settings of
  47. /// an RNB2903. Use this to configure your serial port.
  48. ///
  49. /// Information obtained from Microchip document 40001811 revision B. Timeout is by
  50. /// default set to a very long time; this is sometimes modified on the `SerialPort` itself
  51. /// during certain operations.
  52. pub fn serial_config() -> SerialPortSettings {
  53. SerialPortSettings {
  54. baud_rate: 57600,
  55. data_bits: DataBits::Eight,
  56. flow_control: FlowControl::None,
  57. parity: Parity::None,
  58. stop_bits: StopBits::One,
  59. timeout: Duration::new(1, 0)
  60. }
  61. }
  62. // Once connected to a serial port, the library needs to verify that it is actually
  63. // connected to a RN2903 and not some other serial device. To this end, the `Rn2903`
  64. // wrapper struct's `::new()` function checks the output of the `sys get ver` command,
  65. // which is well-specified.
  66. /// A handle to a serial link connected to a RN2903 module.
  67. ///
  68. /// This library guarantees safety regardless of the state of the RN2903.
  69. pub struct Rn2903 {
  70. port: Box<dyn SerialPort>,
  71. }
  72. impl Rn2903 {
  73. /// Open a new connection to a module at the given path or port name, with the
  74. /// default settings.
  75. pub fn new_at<S: AsRef<OsStr>>(port_name: S) -> Result<Self> {
  76. let sp = serialport::open_with_settings(&port_name, &serial_config())?;
  77. Self::new(sp)
  78. }
  79. /// Open a new connection to a module over the given serial connection.
  80. pub fn new(mut port: Box<dyn SerialPort>) -> Result<Self> {
  81. let mut buf = [0; 35];
  82. port.write_all(b"sys get ver\x0D\x0A")?;
  83. port.flush()?;
  84. thread::sleep(Duration::from_millis(12));
  85. port.read(&mut buf)?;
  86. if &buf[0..6] != b"RN2903" {
  87. Err(Error::WrongDevice((&*String::from_utf8_lossy(&buf)).into()))
  88. } else {
  89. Ok(Self {
  90. port
  91. })
  92. }
  93. }
  94. /// Query the module for its firmware version information.
  95. ///
  96. /// Returns a `String` like `RN2903 1.0.3 Aug 8 2017 15:11:09`
  97. pub fn system_version(&mut self) -> Result<String> {
  98. let mut buf = [0; 35];
  99. self.port.write_all(b"sys get ver\x0D\x0A")?;
  100. self.port.flush()?;
  101. thread::sleep(Duration::from_millis(12));
  102. self.port.read(&mut buf)?;
  103. Ok((&*String::from_utf8_lossy(&buf).trim_end()).into())
  104. }
  105. }