__init__.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850
  1. # python-cc1101 - Python Library to Transmit RF Signals via C1101 Transceivers
  2. #
  3. # Copyright (C) 2020 Fabian Peter Hammerle <fabian@hammerle.me>
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. import contextlib
  18. import enum
  19. import fcntl
  20. import logging
  21. import math
  22. import typing
  23. import spidev
  24. from cc1101.addresses import (
  25. StrobeAddress,
  26. ConfigurationRegisterAddress,
  27. StatusRegisterAddress,
  28. FIFORegisterAddress,
  29. )
  30. from cc1101.options import PacketLengthMode, SyncMode, ModulationFormat
  31. _LOGGER = logging.getLogger(__name__)
  32. class Pin(enum.Enum):
  33. GDO0 = "GDO0"
  34. class _TransceiveMode(enum.IntEnum):
  35. """
  36. PKTCTRL0.PKT_FORMAT
  37. """
  38. FIFO = 0b00
  39. SYNCHRONOUS_SERIAL = 0b01
  40. RANDOM_TRANSMISSION = 0b10
  41. ASYNCHRONOUS_SERIAL = 0b11
  42. class MainRadioControlStateMachineState(enum.IntEnum):
  43. """
  44. MARCSTATE - Main Radio Control State Machine State
  45. """
  46. # see "Figure 13: Simplified State Diagram"
  47. # and "Figure 25: Complete Radio Control State Diagram"
  48. IDLE = 0x01
  49. STARTCAL = 0x08 # after IDLE
  50. BWBOOST = 0x09 # after STARTCAL
  51. FS_LOCK = 0x0A
  52. RX = 0x0D
  53. RXFIFO_OVERFLOW = 0x11
  54. TX = 0x13
  55. # TXFIFO_UNDERFLOW = 0x16
  56. class _ReceivedPacket: # unstable
  57. # "Table 31: Typical RSSI_offset Values"
  58. _RSSI_OFFSET_dB = 74
  59. def __init__(
  60. self,
  61. # *,
  62. data: bytes,
  63. rssi_index: int, # byte
  64. checksum_valid: bool,
  65. link_quality_indicator: int, # 7bit
  66. ):
  67. self.data = data
  68. self._rssi_index = rssi_index
  69. assert 0 <= rssi_index < (1 << 8), rssi_index
  70. self.checksum_valid = checksum_valid
  71. self.link_quality_indicator = link_quality_indicator
  72. assert 0 <= link_quality_indicator < (1 << 7), link_quality_indicator
  73. @property
  74. def rssi_dbm(self) -> float:
  75. """
  76. Estimated Received Signal Strength Indicator (RSSI) in dBm
  77. see section "17.3 RSSI"
  78. """
  79. if self._rssi_index >= 128:
  80. return (self._rssi_index - 256) / 2 - self._RSSI_OFFSET_dB
  81. return self._rssi_index / 2 - self._RSSI_OFFSET_dB
  82. def __str__(self) -> str:
  83. return "{}(RSSI {:.0f}dBm, 0x{})".format(
  84. type(self).__name__,
  85. self.rssi_dbm,
  86. "".join("{:02x}".format(b) for b in self.data),
  87. )
  88. class CC1101:
  89. # pylint: disable=too-many-public-methods
  90. # > All transfers on the SPI interface are done
  91. # > most significant bit first.
  92. # > All transactions on the SPI interface start with
  93. # > a header byte containing a R/W bit, a access bit (B),
  94. # > and a 6-bit address (A5 - A0).
  95. # > [...]
  96. # > Table 45: SPI Address Space
  97. _WRITE_SINGLE_BYTE = 0x00
  98. # > Registers with consecutive addresses can be
  99. # > accessed in an efficient way by setting the
  100. # > burst bit (B) in the header byte. The address
  101. # > bits (A5 - A0) set the start address in an
  102. # > internal address counter. This counter is
  103. # > incremented by one each new byte [...]
  104. _WRITE_BURST = 0x40
  105. _READ_SINGLE_BYTE = 0x80
  106. _READ_BURST = 0xC0
  107. # 29.3 Status Register Details
  108. _SUPPORTED_PARTNUM = 0
  109. _SUPPORTED_VERSION = 0x14
  110. _CRYSTAL_OSCILLATOR_FREQUENCY_HERTZ = 26e6
  111. # see "21 Frequency Programming"
  112. # > f_carrier = f_XOSC / 2**16 * (FREQ + CHAN * ((256 + CHANSPC_M) * 2**CHANSPC_E-2))
  113. _FREQUENCY_CONTROL_WORD_HERTZ_FACTOR = _CRYSTAL_OSCILLATOR_FREQUENCY_HERTZ / 2 ** 16
  114. def __init__(
  115. self, spi_bus: int = 0, spi_chip_select: int = 0, lock_spi_device: bool = False
  116. ) -> None:
  117. """
  118. lock_spi_device:
  119. When True, an advisory, exclusive lock will be set on the SPI device file
  120. non-blockingly via flock upon entering the context.
  121. If the SPI device file is already locked (e.g., by a different process),
  122. a BlockingIOError will be raised.
  123. The lock will be removed automatically, when leaving the context.
  124. The lock can optionally be released earlier by calling .unlock_spi_device().
  125. >>> transceiver = cc1101.CC1101(lock_spi_device=True)
  126. >>> # not locked
  127. >>> with transceiver:
  128. >>> # locked
  129. >>> # lock removed
  130. >>> with transceiver:
  131. >>> # locked
  132. >>> transceiver.unlock_spi_device()
  133. >>> # lock removed
  134. """
  135. self._spi = spidev.SpiDev()
  136. self._spi_bus = int(spi_bus)
  137. # > The BCM2835 core common to all Raspberry Pi devices has 3 SPI Controllers:
  138. # > SPI0, with two hardware chip selects, [...]
  139. # > SPI1, with three hardware chip selects, [...]
  140. # > SPI2, also with three hardware chip selects, is only usable on a Compute Module [...]
  141. # https://github.com/raspberrypi/documentation/blob/d41d69f8efa3667b1a8b01a669238b8bd113edc1/hardware/raspberrypi/spi/README.md#hardware
  142. # https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
  143. self._spi_chip_select = int(spi_chip_select)
  144. self._lock_spi_device = lock_spi_device
  145. @property
  146. def _spi_device_path(self) -> str:
  147. # https://github.com/doceme/py-spidev/blob/v3.4/spidev_module.c#L1286
  148. return "/dev/spidev{}.{}".format(self._spi_bus, self._spi_chip_select)
  149. @staticmethod
  150. def _log_chip_status_byte(chip_status: int) -> None:
  151. # see "10.1 Chip Status Byte" & "Table 23: Status Byte Summary"
  152. # > The command strobe registers are accessed by transferring
  153. # > a single header byte [...]. That is, only the R/W̄ bit,
  154. # > the burst access bit (set to 0), and the six address bits [...]
  155. # > The R/W̄ bit can be either one or zero and will determine how the
  156. # > FIFO_BYTES_AVAILABLE field in the status byte should be interpreted.
  157. _LOGGER.debug(
  158. "chip status byte: CHIP_RDYn=%d STATE=%s FIFO_BYTES_AVAILBLE=%d",
  159. chip_status >> 7,
  160. bin((chip_status >> 4) & 0b111),
  161. chip_status & 0b1111,
  162. )
  163. def _read_single_byte(
  164. self, register: typing.Union[ConfigurationRegisterAddress, FIFORegisterAddress]
  165. ) -> int:
  166. response = self._spi.xfer([register | self._READ_SINGLE_BYTE, 0])
  167. assert len(response) == 2, response
  168. self._log_chip_status_byte(response[0])
  169. return response[1]
  170. def _read_burst(
  171. self,
  172. start_register: typing.Union[ConfigurationRegisterAddress, FIFORegisterAddress],
  173. length: int,
  174. ) -> typing.List[int]:
  175. response = self._spi.xfer([start_register | self._READ_BURST] + [0] * length)
  176. assert len(response) == length + 1, response
  177. self._log_chip_status_byte(response[0])
  178. return response[1:]
  179. def _read_status_register(self, register: StatusRegisterAddress) -> int:
  180. # > For register addresses in the range 0x30-0x3D,
  181. # > the burst bit is used to select between
  182. # > status registers when burst bit is one, and
  183. # > between command strobes when burst bit is
  184. # > zero. [...]
  185. # > Because of this, burst access is not available
  186. # > for status registers and they must be accessed
  187. # > one at a time. The status registers can only be
  188. # > read.
  189. response = self._spi.xfer([register | self._READ_BURST, 0])
  190. assert len(response) == 2, response
  191. self._log_chip_status_byte(response[0])
  192. return response[1]
  193. def _command_strobe(self, register: StrobeAddress) -> None:
  194. # see "10.4 Command Strobes"
  195. _LOGGER.debug("sending command strobe 0x%02x", register)
  196. response = self._spi.xfer([register | self._WRITE_SINGLE_BYTE])
  197. assert len(response) == 1, response
  198. self._log_chip_status_byte(response[0])
  199. def _write_burst(
  200. self,
  201. start_register: typing.Union[ConfigurationRegisterAddress, FIFORegisterAddress],
  202. values: typing.List[int],
  203. ) -> None:
  204. _LOGGER.debug(
  205. "writing burst: start_register=0x%02x values=%s", start_register, values
  206. )
  207. response = self._spi.xfer([start_register | self._WRITE_BURST] + values)
  208. assert len(response) == len(values) + 1, response
  209. self._log_chip_status_byte(response[0])
  210. assert all(v == response[0] for v in response[1:]), response
  211. def _reset(self) -> None:
  212. self._command_strobe(StrobeAddress.SRES)
  213. @classmethod
  214. def _filter_bandwidth_floating_point_to_real(
  215. cls, mantissa: int, exponent: int
  216. ) -> float:
  217. """
  218. See "13 Receiver Channel Filter Bandwidth"
  219. """
  220. return cls._CRYSTAL_OSCILLATOR_FREQUENCY_HERTZ / (
  221. 8 * (4 + mantissa) * (2 ** exponent)
  222. )
  223. def _get_filter_bandwidth_hertz(self) -> float:
  224. """
  225. See "13 Receiver Channel Filter Bandwidth"
  226. MDMCFG4.CHANBW_E & MDMCFG4.CHANBW_M
  227. """
  228. mdmcfg4 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG4)
  229. return self._filter_bandwidth_floating_point_to_real(
  230. exponent=mdmcfg4 >> 6, mantissa=(mdmcfg4 >> 4) & 0b11
  231. )
  232. def _set_filter_bandwidth(self, *, mantissa: int, exponent: int) -> None:
  233. """
  234. MDMCFG4.CHANBW_E & MDMCFG4.CHANBW_M
  235. """
  236. mdmcfg4 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG4)
  237. mdmcfg4 &= 0b00001111
  238. assert 0 <= exponent <= 0b11, exponent
  239. mdmcfg4 |= exponent << 6
  240. assert 0 <= mantissa <= 0b11, mantissa
  241. mdmcfg4 |= mantissa << 4
  242. self._write_burst(
  243. start_register=ConfigurationRegisterAddress.MDMCFG4, values=[mdmcfg4]
  244. )
  245. def _get_symbol_rate_exponent(self) -> int:
  246. """
  247. MDMCFG4.DRATE_E
  248. """
  249. return self._read_single_byte(ConfigurationRegisterAddress.MDMCFG4) & 0b00001111
  250. def _set_symbol_rate_exponent(self, exponent: int):
  251. mdmcfg4 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG4)
  252. mdmcfg4 &= 0b11110000
  253. mdmcfg4 |= exponent
  254. self._write_burst(
  255. start_register=ConfigurationRegisterAddress.MDMCFG4, values=[mdmcfg4]
  256. )
  257. def _get_symbol_rate_mantissa(self) -> int:
  258. """
  259. MDMCFG3.DRATE_M
  260. """
  261. return self._read_single_byte(ConfigurationRegisterAddress.MDMCFG3)
  262. def _set_symbol_rate_mantissa(self, mantissa: int) -> None:
  263. self._write_burst(
  264. start_register=ConfigurationRegisterAddress.MDMCFG3, values=[mantissa]
  265. )
  266. @classmethod
  267. def _symbol_rate_floating_point_to_real(cls, mantissa: int, exponent: int) -> float:
  268. # see "12 Data Rate Programming"
  269. return (
  270. (256 + mantissa)
  271. * (2 ** exponent)
  272. * cls._CRYSTAL_OSCILLATOR_FREQUENCY_HERTZ
  273. / (2 ** 28)
  274. )
  275. @classmethod
  276. def _symbol_rate_real_to_floating_point(cls, real: float) -> typing.Tuple[int, int]:
  277. # see "12 Data Rate Programming"
  278. assert real > 0, real
  279. exponent = math.floor(
  280. math.log2(real / cls._CRYSTAL_OSCILLATOR_FREQUENCY_HERTZ) + 20
  281. )
  282. mantissa = round(
  283. real * 2 ** 28 / cls._CRYSTAL_OSCILLATOR_FREQUENCY_HERTZ / 2 ** exponent
  284. - 256
  285. )
  286. if mantissa == 256:
  287. exponent += 1
  288. mantissa = 0
  289. assert 0 < exponent <= 2 ** 4, exponent
  290. assert mantissa <= 2 ** 8, mantissa
  291. return mantissa, exponent
  292. def get_symbol_rate_baud(self) -> float:
  293. return self._symbol_rate_floating_point_to_real(
  294. mantissa=self._get_symbol_rate_mantissa(),
  295. exponent=self._get_symbol_rate_exponent(),
  296. )
  297. def set_symbol_rate_baud(self, real: float) -> None:
  298. # > The data rate can be set from 0.6 kBaud to 500 kBaud [...]
  299. mantissa, exponent = self._symbol_rate_real_to_floating_point(real)
  300. self._set_symbol_rate_mantissa(mantissa)
  301. self._set_symbol_rate_exponent(exponent)
  302. def get_modulation_format(self) -> ModulationFormat:
  303. mdmcfg2 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG2)
  304. return ModulationFormat((mdmcfg2 >> 4) & 0b111)
  305. def _set_modulation_format(self, modulation_format: ModulationFormat) -> None:
  306. mdmcfg2 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG2)
  307. mdmcfg2 &= ~(modulation_format << 4)
  308. mdmcfg2 |= modulation_format << 4
  309. self._write_burst(ConfigurationRegisterAddress.MDMCFG2, [mdmcfg2])
  310. def enable_manchester_code(self) -> None:
  311. """
  312. MDMCFG2.MANCHESTER_EN
  313. Enable manchester encoding & decoding for the entire packet,
  314. including the preamble and synchronization word.
  315. """
  316. mdmcfg2 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG2)
  317. mdmcfg2 |= 0b1000
  318. self._write_burst(ConfigurationRegisterAddress.MDMCFG2, [mdmcfg2])
  319. def get_sync_mode(self) -> SyncMode:
  320. mdmcfg2 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG2)
  321. return SyncMode(mdmcfg2 & 0b11)
  322. def set_sync_mode(
  323. self,
  324. mode: SyncMode,
  325. *,
  326. _carrier_sense_threshold_enabled: typing.Optional[bool] = None # unstable
  327. ) -> None:
  328. """
  329. MDMCFG2.SYNC_MODE
  330. see "14.3 Byte Synchronization"
  331. Carrier Sense (CS) Threshold (when receiving packets, API unstable):
  332. > Carrier sense can be used as a sync word qualifier
  333. > that requires the signal level to be higher than the threshold
  334. > for a sync word > search to be performed [...]
  335. > CS can be used to avoid interference from other RF sources [...]
  336. True: enable, False: disable, None: keep current setting
  337. See "17.4 Carrier Sense (CS)"
  338. """
  339. mdmcfg2 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG2)
  340. mdmcfg2 &= 0b11111100
  341. mdmcfg2 |= mode
  342. if _carrier_sense_threshold_enabled is not None:
  343. if _carrier_sense_threshold_enabled:
  344. mdmcfg2 |= 0b00000100
  345. else:
  346. mdmcfg2 &= 0b11111011
  347. self._write_burst(ConfigurationRegisterAddress.MDMCFG2, [mdmcfg2])
  348. def get_preamble_length_bytes(self) -> int:
  349. """
  350. MDMCFG1.NUM_PREAMBLE
  351. Minimum number of preamble bytes to be transmitted.
  352. See "15.2 Packet Format"
  353. """
  354. index = (
  355. self._read_single_byte(ConfigurationRegisterAddress.MDMCFG1) >> 4
  356. ) & 0b111
  357. return 2 ** (index >> 1) * (2 + (index & 0b1))
  358. def _set_preamble_length_index(self, index: int) -> None:
  359. assert 0 <= index <= 0b111
  360. mdmcfg1 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG1)
  361. mdmcfg1 &= 0b10001111
  362. mdmcfg1 |= index << 4
  363. self._write_burst(ConfigurationRegisterAddress.MDMCFG1, [mdmcfg1])
  364. def set_preamble_length_bytes(self, length: int) -> None:
  365. """
  366. see .get_preamble_length_bytes()
  367. """
  368. if length < 1:
  369. raise ValueError(
  370. "invalid preamble length {} given".format(length)
  371. + "\ncall .set_sync_mode(cc1101.SyncMode.NO_PREAMBLE_AND_SYNC_WORD)"
  372. + " to disable preamble"
  373. )
  374. if length % 3 == 0:
  375. index = math.log2(length / 3) * 2 + 1
  376. else:
  377. index = math.log2(length / 2) * 2
  378. if not index.is_integer() or index < 0 or index > 0b111:
  379. raise ValueError(
  380. "unsupported preamble length: {} bytes".format(length)
  381. + "\nsee MDMCFG1.NUM_PREAMBLE in cc1101 docs"
  382. )
  383. self._set_preamble_length_index(int(index))
  384. def _set_power_amplifier_setting_index(self, setting_index: int) -> None:
  385. """
  386. FREND0.PA_POWER
  387. > This value is an index to the PATABLE,
  388. > which can be programmed with up to 8 different PA settings.
  389. > In OOK/ASK mode, this selects the PATABLE index to use
  390. > when transmitting a '1'.
  391. > PATABLE index zero is used in OOK/ASK when transmitting a '0'.
  392. > The PATABLE settings from index 0 to the PA_POWER value are
  393. > used for > ASK TX shaping, [...]
  394. see "Figure 32: Shaping of ASK Signal"
  395. > If OOK modulation is used, the logic 0 and logic 1 power levels
  396. > shall be programmed to index 0 and 1 respectively.
  397. """
  398. frend0 = self._read_single_byte(ConfigurationRegisterAddress.FREND0)
  399. frend0 &= 0b000
  400. frend0 |= setting_index
  401. self._write_burst(ConfigurationRegisterAddress.FREND0, [setting_index])
  402. def _verify_chip(self) -> None:
  403. partnum = self._read_status_register(StatusRegisterAddress.PARTNUM)
  404. if partnum != self._SUPPORTED_PARTNUM:
  405. raise ValueError(
  406. "unexpected chip part number {} (expected: {})".format(
  407. partnum, self._SUPPORTED_PARTNUM
  408. )
  409. )
  410. version = self._read_status_register(StatusRegisterAddress.VERSION)
  411. if version != self._SUPPORTED_VERSION:
  412. raise ValueError(
  413. "unexpected chip version number {} (expected: {})".format(
  414. version, self._SUPPORTED_VERSION
  415. )
  416. )
  417. def _configure_defaults(self) -> None:
  418. # 6:4 MOD_FORMAT: OOK (default: 2-FSK)
  419. self._set_modulation_format(ModulationFormat.ASK_OOK)
  420. self._set_power_amplifier_setting_index(1)
  421. self._disable_data_whitening()
  422. # 7:6 unused
  423. # 5:4 FS_AUTOCAL: calibrate when going from IDLE to RX or TX
  424. # 3:2 PO_TIMEOUT: default
  425. # 1 PIN_CTRL_EN: default
  426. # 0 XOSC_FORCE_ON: default
  427. self._write_burst(ConfigurationRegisterAddress.MCSM0, [0b010100])
  428. def __enter__(self) -> "CC1101":
  429. # https://docs.python.org/3/reference/datamodel.html#object.__enter__
  430. try:
  431. self._spi.open(self._spi_bus, self._spi_chip_select)
  432. except PermissionError as exc:
  433. raise PermissionError(
  434. "Could not access {}".format(self._spi_device_path)
  435. + "\nVerify that the current user has both read and write access."
  436. + "\nOn some devices, like Raspberry Pis,"
  437. + "\n\tsudo usermod -a -G spi $USER"
  438. + "\nfollowed by a re-login grants sufficient permissions."
  439. ) from exc
  440. if self._lock_spi_device:
  441. # advisory, exclusive, non-blocking
  442. # lock removed in __exit__ by SpiDev.close()
  443. fcntl.flock(self._spi.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
  444. self._spi.max_speed_hz = 55700 # empirical
  445. self._reset()
  446. self._verify_chip()
  447. self._configure_defaults()
  448. marcstate = self.get_main_radio_control_state_machine_state()
  449. if marcstate != MainRadioControlStateMachineState.IDLE:
  450. raise ValueError("expected marcstate idle (actual: {})".format(marcstate))
  451. return self
  452. def __exit__(self, exc_type, exc_value, traceback): # -> typing.Literal[False]
  453. # https://docs.python.org/3/reference/datamodel.html#object.__exit__
  454. self._spi.close()
  455. return False
  456. def unlock_spi_device(self) -> None:
  457. """
  458. Manually release the lock set on the SPI device file.
  459. Alternatively, the lock will be released automatically,
  460. when leaving the context.
  461. Method fails silently, if the SPI device file is not locked.
  462. >>> transceiver = cc1101.CC1101(lock_spi_device=True)
  463. >>> # not locked
  464. >>> with transceiver:
  465. >>> # locked
  466. >>> # lock removed
  467. >>> with transceiver:
  468. >>> # locked
  469. >>> transceiver.unlock_spi_device()
  470. >>> # lock removed
  471. """
  472. fileno = self._spi.fileno()
  473. if fileno != -1:
  474. fcntl.flock(fileno, fcntl.LOCK_UN)
  475. def get_main_radio_control_state_machine_state(
  476. self,
  477. ) -> MainRadioControlStateMachineState:
  478. return MainRadioControlStateMachineState(
  479. self._read_status_register(StatusRegisterAddress.MARCSTATE)
  480. )
  481. def get_marc_state(self) -> MainRadioControlStateMachineState:
  482. """
  483. alias for get_main_radio_control_state_machine_state()
  484. """
  485. return self.get_main_radio_control_state_machine_state()
  486. @classmethod
  487. def _frequency_control_word_to_hertz(cls, control_word: typing.List[int]) -> float:
  488. return (
  489. int.from_bytes(control_word, byteorder="big", signed=False)
  490. * cls._FREQUENCY_CONTROL_WORD_HERTZ_FACTOR
  491. )
  492. @classmethod
  493. def _hertz_to_frequency_control_word(cls, hertz: float) -> typing.List[int]:
  494. return list(
  495. round(hertz / cls._FREQUENCY_CONTROL_WORD_HERTZ_FACTOR).to_bytes(
  496. length=3, byteorder="big", signed=False
  497. )
  498. )
  499. def _get_base_frequency_control_word(self) -> typing.List[int]:
  500. # > The base or start frequency is set by the 24 bitfrequency
  501. # > word located in the FREQ2, FREQ1, FREQ0 registers.
  502. return self._read_burst(
  503. start_register=ConfigurationRegisterAddress.FREQ2, length=3
  504. )
  505. def _set_base_frequency_control_word(self, control_word: typing.List[int]) -> None:
  506. self._write_burst(
  507. start_register=ConfigurationRegisterAddress.FREQ2, values=control_word
  508. )
  509. def get_base_frequency_hertz(self) -> float:
  510. return self._frequency_control_word_to_hertz(
  511. self._get_base_frequency_control_word()
  512. )
  513. def set_base_frequency_hertz(self, freq: float) -> None:
  514. self._set_base_frequency_control_word(
  515. self._hertz_to_frequency_control_word(freq)
  516. )
  517. def __str__(self) -> str:
  518. sync_mode = self.get_sync_mode()
  519. attrs = (
  520. "marcstate={}".format(
  521. self.get_main_radio_control_state_machine_state().name.lower()
  522. ),
  523. "base_frequency={:.2f}MHz".format(
  524. self.get_base_frequency_hertz() / 10 ** 6
  525. ),
  526. "symbol_rate={:.2f}kBaud".format(self.get_symbol_rate_baud() / 1000),
  527. "modulation_format={}".format(self.get_modulation_format().name),
  528. "sync_mode={}".format(sync_mode.name),
  529. "preamble_length={}B".format(self.get_preamble_length_bytes())
  530. if sync_mode != SyncMode.NO_PREAMBLE_AND_SYNC_WORD
  531. else None,
  532. "sync_word=0x{:02x}{:02x}".format(*self.get_sync_word())
  533. if sync_mode != SyncMode.NO_PREAMBLE_AND_SYNC_WORD
  534. else None,
  535. "packet_length{}{}B".format(
  536. "≤"
  537. if self.get_packet_length_mode() == PacketLengthMode.VARIABLE
  538. else "=",
  539. self.get_packet_length_bytes(),
  540. ),
  541. )
  542. return "CC1101({})".format(", ".join(filter(None, attrs)))
  543. def get_configuration_register_values(
  544. self,
  545. start_register: ConfigurationRegisterAddress = min(
  546. ConfigurationRegisterAddress
  547. ),
  548. end_register: ConfigurationRegisterAddress = max(ConfigurationRegisterAddress),
  549. ) -> typing.Dict[ConfigurationRegisterAddress, int]:
  550. assert start_register <= end_register, (start_register, end_register)
  551. values = self._read_burst(
  552. start_register=start_register, length=end_register - start_register + 1
  553. )
  554. return {
  555. ConfigurationRegisterAddress(start_register + i): v
  556. for i, v in enumerate(values)
  557. }
  558. def get_sync_word(self) -> bytes:
  559. """
  560. SYNC1 & SYNC0
  561. See "15.2 Packet Format"
  562. The first byte's most significant bit is transmitted first.
  563. """
  564. return bytes(
  565. self._read_burst(
  566. start_register=ConfigurationRegisterAddress.SYNC1, length=2
  567. )
  568. )
  569. def set_sync_word(self, sync_word: bytes) -> None:
  570. """
  571. See .set_sync_word()
  572. """
  573. if len(sync_word) != 2:
  574. raise ValueError("expected two bytes, got {!r}".format(sync_word))
  575. self._write_burst(
  576. start_register=ConfigurationRegisterAddress.SYNC1, values=list(sync_word)
  577. )
  578. def get_packet_length_bytes(self) -> int:
  579. """
  580. PKTLEN
  581. Packet length in fixed packet length mode,
  582. maximum packet length in variable packet length mode.
  583. > In variable packet length mode, [...]
  584. > any packet received with a length byte
  585. > with a value greater than PKTLEN will be discarded.
  586. """
  587. return self._read_single_byte(ConfigurationRegisterAddress.PKTLEN)
  588. def set_packet_length_bytes(self, packet_length: int) -> None:
  589. """
  590. see get_packet_length_bytes()
  591. """
  592. assert 1 <= packet_length <= 255, "unsupported packet length {}".format(
  593. packet_length
  594. )
  595. self._write_burst(
  596. start_register=ConfigurationRegisterAddress.PKTLEN, values=[packet_length]
  597. )
  598. def _disable_data_whitening(self):
  599. """
  600. PKTCTRL0.WHITE_DATA
  601. see "15.1 Data Whitening"
  602. > By setting PKTCTRL0.WHITE_DATA=1 [default],
  603. > all data, except the preamble and the sync word
  604. > will be XOR-ed with a 9-bit pseudo-random (PN9)
  605. > sequence before being transmitted.
  606. """
  607. pktctrl0 = self._read_single_byte(ConfigurationRegisterAddress.PKTCTRL0)
  608. pktctrl0 &= 0b10111111
  609. self._write_burst(
  610. start_register=ConfigurationRegisterAddress.PKTCTRL0, values=[pktctrl0]
  611. )
  612. def disable_checksum(self) -> None:
  613. """
  614. PKTCTRL0.CRC_EN
  615. Disable automatic 2-byte cyclic redundancy check (CRC) sum
  616. appending in TX mode and checking in RX mode.
  617. See "Figure 19: Packet Format".
  618. """
  619. pktctrl0 = self._read_single_byte(ConfigurationRegisterAddress.PKTCTRL0)
  620. pktctrl0 &= 0b11111011
  621. self._write_burst(
  622. start_register=ConfigurationRegisterAddress.PKTCTRL0, values=[pktctrl0]
  623. )
  624. def _get_transceive_mode(self) -> _TransceiveMode:
  625. pktctrl0 = self._read_single_byte(ConfigurationRegisterAddress.PKTCTRL0)
  626. return _TransceiveMode((pktctrl0 >> 4) & 0b11)
  627. def _set_transceive_mode(self, mode: _TransceiveMode) -> None:
  628. _LOGGER.info("changing transceive mode to %s", mode.name)
  629. pktctrl0 = self._read_single_byte(ConfigurationRegisterAddress.PKTCTRL0)
  630. pktctrl0 &= ~0b00110000
  631. pktctrl0 |= mode << 4
  632. self._write_burst(
  633. start_register=ConfigurationRegisterAddress.PKTCTRL0, values=[pktctrl0]
  634. )
  635. def get_packet_length_mode(self) -> PacketLengthMode:
  636. pktctrl0 = self._read_single_byte(ConfigurationRegisterAddress.PKTCTRL0)
  637. return PacketLengthMode(pktctrl0 & 0b11)
  638. def set_packet_length_mode(self, mode: PacketLengthMode) -> None:
  639. pktctrl0 = self._read_single_byte(ConfigurationRegisterAddress.PKTCTRL0)
  640. pktctrl0 &= 0b11111100
  641. pktctrl0 |= mode
  642. self._write_burst(
  643. start_register=ConfigurationRegisterAddress.PKTCTRL0, values=[pktctrl0]
  644. )
  645. def _flush_tx_fifo_buffer(self) -> None:
  646. # > Only issue SFTX in IDLE or TXFIFO_UNDERFLOW states.
  647. _LOGGER.debug("flushing tx fifo buffer")
  648. self._command_strobe(StrobeAddress.SFTX)
  649. def transmit(self, payload: bytes) -> None:
  650. """
  651. The most significant bit is transmitted first.
  652. In variable packet length mode,
  653. a byte indicating the packet's length will be prepended.
  654. > In variable packet length mode,
  655. > the packet length is configured by the first byte [...].
  656. > The packet length is defined as the payload data,
  657. > excluding the length byte and the optional CRC.
  658. from "15.2 Packet Format"
  659. Call .set_packet_length_mode(cc1101.PacketLengthMode.FIXED)
  660. to switch to fixed packet length mode.
  661. """
  662. # see "15.2 Packet Format"
  663. # > In variable packet length mode, [...]
  664. # > The first byte written to the TXFIFO must be different from 0.
  665. packet_length_mode = self.get_packet_length_mode()
  666. packet_length = self.get_packet_length_bytes()
  667. if packet_length_mode == PacketLengthMode.VARIABLE:
  668. if not payload:
  669. raise ValueError("empty payload {!r}".format(payload))
  670. if len(payload) > packet_length:
  671. raise ValueError(
  672. "payload exceeds maximum payload length of {} bytes".format(
  673. packet_length
  674. )
  675. + "\nsee .get_packet_length_bytes()"
  676. + "\npayload: {!r}".format(payload)
  677. )
  678. payload = int.to_bytes(len(payload), length=1, byteorder="big") + payload
  679. elif (
  680. packet_length_mode == PacketLengthMode.FIXED
  681. and len(payload) != packet_length
  682. ):
  683. raise ValueError(
  684. "expected payload length of {} bytes, got {}".format(
  685. packet_length, len(payload)
  686. )
  687. + "\nsee .set_packet_length_mode() and .get_packet_length_bytes()"
  688. + "\npayload: {!r}".format(payload)
  689. )
  690. marcstate = self.get_main_radio_control_state_machine_state()
  691. if marcstate != MainRadioControlStateMachineState.IDLE:
  692. raise Exception(
  693. "device must be idle before transmission (current marcstate: {})".format(
  694. marcstate.name
  695. )
  696. )
  697. self._flush_tx_fifo_buffer()
  698. self._write_burst(FIFORegisterAddress.TX, list(payload))
  699. _LOGGER.info(
  700. "transmitting 0x%s (%r)",
  701. "".join("{:02x}".format(b) for b in payload),
  702. payload,
  703. )
  704. self._command_strobe(StrobeAddress.STX)
  705. @contextlib.contextmanager
  706. def asynchronous_transmission(self) -> typing.Iterator[Pin]:
  707. """
  708. see "27.1 Asynchronous Serial Operation"
  709. >>> with cc1101.CC1101() as transceiver:
  710. >>> transceiver.set_base_frequency_hertz(433.92e6)
  711. >>> transceiver.set_symbol_rate_baud(600)
  712. >>> print(transceiver)
  713. >>> with transceiver.asynchronous_transmission():
  714. >>> # send digital signal to GDO0 pin
  715. """
  716. self._set_transceive_mode(_TransceiveMode.ASYNCHRONOUS_SERIAL)
  717. self._command_strobe(StrobeAddress.STX)
  718. try:
  719. # > In TX, the GDO0 pin is used for data input (TX data).
  720. yield Pin.GDO0
  721. finally:
  722. self._command_strobe(StrobeAddress.SIDLE)
  723. self._set_transceive_mode(_TransceiveMode.FIFO)
  724. def _enable_receive_mode(self) -> None: # unstable
  725. self._command_strobe(StrobeAddress.SRX)
  726. def _get_received_packet(self) -> typing.Optional[_ReceivedPacket]: # unstable
  727. """
  728. see section "20 Data FIFO"
  729. """
  730. rxbytes = self._read_status_register(StatusRegisterAddress.RXBYTES)
  731. # PKTCTRL1.APPEND_STATUS is enabled by default
  732. if rxbytes < 2:
  733. return None
  734. buffer = self._read_burst(start_register=FIFORegisterAddress.RX, length=rxbytes)
  735. return _ReceivedPacket(
  736. data=bytes(buffer[:-2]),
  737. rssi_index=buffer[-2],
  738. checksum_valid=bool(buffer[-1] >> 7),
  739. link_quality_indicator=buffer[-1] & 0b0111111,
  740. )