mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-19 16:56:34 +00:00
Prep LoRa 2
This commit is contained in:
parent
d4980b9957
commit
d258b9a758
25
lib/libesp32/RadioLib/.gitignore
vendored
Normal file
25
lib/libesp32/RadioLib/.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# Arduino Library Development file
|
||||
.development
|
||||
|
||||
# Atom
|
||||
*.tags
|
||||
*.tags1
|
||||
|
||||
# VS Code
|
||||
.vscode
|
||||
|
||||
# Jetbrain IDEs
|
||||
.idea
|
||||
|
||||
# Debug decoder
|
||||
extras/decoder/log.txt
|
||||
extras/decoder/out.txt
|
||||
|
||||
# Spectrum scan
|
||||
extras/SX126x_Spectrum_Scan/out/*
|
||||
|
||||
# PlatformIO
|
||||
.pio*
|
||||
|
||||
# cmake
|
||||
build/
|
3
lib/libesp32/RadioLib/.gitmodules
vendored
Normal file
3
lib/libesp32/RadioLib/.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "examples/NonArduino/Tock/libtock-c"]
|
||||
path = examples/NonArduino/Tock/libtock-c
|
||||
url = https://github.com/tock/libtock-c.git
|
45
lib/libesp32/RadioLib/CMakeLists.txt
Normal file
45
lib/libesp32/RadioLib/CMakeLists.txt
Normal file
@ -0,0 +1,45 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
if(ESP_PLATFORM)
|
||||
# Build RadioLib as an ESP-IDF component
|
||||
# required because ESP-IDF runs cmake in script mode
|
||||
# and needs idf_component_register()
|
||||
file(GLOB_RECURSE RADIOLIB_ESP_SOURCES
|
||||
"src/*.*"
|
||||
)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${RADIOLIB_ESP_SOURCES}
|
||||
INCLUDE_DIRS . src
|
||||
)
|
||||
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(CMAKE_SCRIPT_MODE_FILE)
|
||||
message(FATAL_ERROR "Attempted to build RadioLib in script mode")
|
||||
endif()
|
||||
|
||||
project(radiolib)
|
||||
|
||||
file(GLOB_RECURSE RADIOLIB_SOURCES
|
||||
"src/*.cpp"
|
||||
)
|
||||
|
||||
add_library(RadioLib ${RADIOLIB_SOURCES})
|
||||
|
||||
target_include_directories(RadioLib
|
||||
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
install(TARGETS RadioLib
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/RadioLib
|
||||
FILES_MATCHING PATTERN "*.h"
|
||||
)
|
3
lib/libesp32/RadioLib/CODE_OF_CONDUCT.md
Normal file
3
lib/libesp32/RadioLib/CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Code of Conduct
|
||||
|
||||
Don't be an a*shole.
|
110
lib/libesp32/RadioLib/CONTRIBUTING.md
Normal file
110
lib/libesp32/RadioLib/CONTRIBUTING.md
Normal file
@ -0,0 +1,110 @@
|
||||
# Contributing to RadioLib
|
||||
|
||||
First of all, thank you very much for taking the time to contribute! All feedback and ideas are greatly appreciated.
|
||||
To keep this library organized, please follow these rules.
|
||||
|
||||
## Issues
|
||||
|
||||
The following rules guide submission of new issues. These rules are in place mainly so that the issue author can get help as quickly as possible.
|
||||
|
||||
1. **Questions are welcome, spam is not.**
|
||||
Any issues without description will be considered spam and as such will be **CLOSED** and **LOCKED** immediately!
|
||||
2. **This repository has issue templates.**
|
||||
To report bugs or suggest new features, use the provided issue templates. Use the default issue only if the templates do not fit your issue type.
|
||||
3. **Be as clear as possible when creating issues.**
|
||||
Issues with generic titles (e.g. "not working", "lora", etc.) will be **CLOSED** until the title is fixed, since the title is supposed to categorize the issue. The same applies for issues with very little information and extensive grammatical or formatting errors that make it difficult to find out what is the actual issue.
|
||||
4. **Issues deserve some attention too.**
|
||||
Issues that are left for 2 weeks without response by the original author when asked for further information will be closed due to inactivity. This is to keep track of important issues, the author is encouraged to reopen the issue at a later date.
|
||||
|
||||
## Code style guidelines
|
||||
|
||||
I like pretty code! Or at least, I like *consistent* code style. When creating pull requests, please follow these style guidelines, they're in place to keep high code readability.
|
||||
|
||||
1. **Bracket style**
|
||||
This library uses the following style of bracket indentation (1TBS, or "javascript" style):
|
||||
|
||||
```c++
|
||||
if (foo) {
|
||||
bar();
|
||||
} else {
|
||||
baz();
|
||||
}
|
||||
```
|
||||
|
||||
2. **Tabs**
|
||||
Use 2 space characters for tabs.
|
||||
|
||||
3. **Single-line comments**
|
||||
Comments can be very useful - and they can become the bane of readability. Every single-line comment should start at new line, have one space between comment delimiter `//` and the start of the comment itself. The comment should also start with a lower-case letter.
|
||||
|
||||
```c++
|
||||
// this function does something
|
||||
foo("bar");
|
||||
|
||||
// here it does something else
|
||||
foo(12345);
|
||||
```
|
||||
|
||||
4. **Split code into blocks**
|
||||
It is very easy to write code that machine can read. It is much harder to write one that humans can read. That's why it's a great idea to split code into blocks - even if the block is just a single line!
|
||||
|
||||
```c++
|
||||
// build a temporary buffer (first block)
|
||||
uint8_t* data = new uint8_t[len + 1];
|
||||
if(!data) {
|
||||
return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED);
|
||||
}
|
||||
|
||||
// read the received data (second block)
|
||||
state = readData(data, len);
|
||||
|
||||
// add null terminator (third block)
|
||||
data[len] = 0;
|
||||
```
|
||||
|
||||
5. **Doxygen**
|
||||
If you're adding a new method, make sure to add appropriate Doxygen comments, so that the documentation is always complete.
|
||||
|
||||
6. **Keywords**
|
||||
This is an Arduino library, so it needs to comply with the Arduino library specification. To add a new keyword to the Arduino IDE syntax highlighting, add it to the keywords.txt file. **Use true tabs in keywords.txt! No spaces there!**
|
||||
|
||||
7. **Dynamic memory**
|
||||
Sometimes, RadioLib might be used in critical applications where dynamic memory allocation using `new` or `malloc` might cause issues. For such cases, RadioLib provides the option to compile using only static arrays. This means that every dynamically allocated array must have a sufficiently large static counterpart. Naturally, all dynamically allocated memory must be properly de-allocated using `delete` or `free`.
|
||||
|
||||
```c++
|
||||
// build a temporary buffer
|
||||
#if defined(RADIOLIB_STATIC_ONLY)
|
||||
uint8_t data[RADIOLIB_STATIC_ARRAY_SIZE + 1];
|
||||
#else
|
||||
uint8_t* data = new uint8_t[length + 1];
|
||||
if(!data) {
|
||||
return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED);
|
||||
}
|
||||
#endif
|
||||
|
||||
// read the received data
|
||||
readData(data, length);
|
||||
|
||||
// deallocate temporary buffer
|
||||
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||
delete[] data;
|
||||
#endif
|
||||
```
|
||||
|
||||
8. **God Mode**
|
||||
During development, it can be useful to have access to the low level drivers, such as the SPI commands. These are incredibly powerful, since they will basically let user do anything he wants with the module, outside of the normal level of sanity checks. As such, they are normally protected using C++ access modifiers `private` or `protected`. God mode disables this protection, and so any newly implemented `class` must contain the appropriate macro check:
|
||||
|
||||
```c++
|
||||
class Module {
|
||||
void publicMethod();
|
||||
|
||||
#if defined(RADIOLIB_GODMODE)
|
||||
private:
|
||||
#endif
|
||||
|
||||
void privateMethod();
|
||||
};
|
||||
```
|
||||
|
||||
9. **No Arduino Strings**
|
||||
Arduino `String` class should never be used internally in the library. The only allowed occurence of Arduino `String` is in public API methods, and only at the top-most layer.
|
2566
lib/libesp32/RadioLib/Doxyfile
Normal file
2566
lib/libesp32/RadioLib/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
91
lib/libesp32/RadioLib/README.md
Normal file
91
lib/libesp32/RadioLib/README.md
Normal file
@ -0,0 +1,91 @@
|
||||
# RadioLib  [](https://registry.platformio.org/libraries/jgromes/RadioLib)
|
||||
|
||||
### _One radio library to rule them all!_
|
||||
|
||||
## Universal wireless communication library for embedded devices
|
||||
|
||||
## See the [Wiki](https://github.com/jgromes/RadioLib/wiki) and [FAQ](https://github.com/jgromes/RadioLib/wiki/Frequently-Asked-Questions) for further information. See the [GitHub Pages](https://jgromes.github.io/RadioLib) for detailed and up-to-date API reference.
|
||||
|
||||
RadioLib allows its users to integrate all sorts of different wireless communication modules, protocols and even digital modes into a single consistent system.
|
||||
Want to add a Bluetooth interface to your LoRa network? Sure thing! Do you just want to go really old-school and play around with radio teletype, slow-scan TV, or even Hellschreiber using nothing but a cheap radio module? Why not!
|
||||
|
||||
RadioLib natively supports Arduino, but can run in non-Arduino environments as well! See [this Wiki page](https://github.com/jgromes/RadioLib/wiki/Porting-to-non-Arduino-Platforms) and [examples/NonArduino](https://github.com/jgromes/RadioLib/tree/master/examples/NonArduino).
|
||||
|
||||
RadioLib was originally created as a driver for [__RadioShield__](https://github.com/jgromes/RadioShield), but it can be used to control as many different wireless modules as you like - or at least as many as your microcontroller can handle!
|
||||
|
||||
### Supported modules:
|
||||
* __CC1101__ FSK radio module
|
||||
* __LLCC68__ LoRa module
|
||||
* __nRF24L01__ 2.4 GHz module
|
||||
* __RF69__ FSK/OOK radio module
|
||||
* __RFM2x__ series FSK modules (RFM22, RM23)
|
||||
* __RFM9x__ series LoRa modules (RFM95, RM96, RFM97, RFM98)
|
||||
* __Si443x__ series FSK modules (Si4430, Si4431, Si4432)
|
||||
* __STM32WL__ integrated microcontroller/LoRa module
|
||||
* __SX126x__ series LoRa modules (SX1261, SX1262, SX1268)
|
||||
* __SX127x__ series LoRa modules (SX1272, SX1273, SX1276, SX1277, SX1278, SX1279)
|
||||
* __SX128x__ series LoRa/GFSK/BLE/FLRC modules (SX1280, SX1281, SX1282)
|
||||
* __SX123x__ FSK/OOK radio modules (SX1231, SX1233)
|
||||
|
||||
### Supported protocols and digital modes:
|
||||
* [__AX.25__](https://www.sigidwiki.com/wiki/PACKET) using 2-FSK or AFSK for modules:
|
||||
SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, RFM2x and Si443x
|
||||
* [__RTTY__](https://www.sigidwiki.com/wiki/RTTY) using 2-FSK or AFSK for modules:
|
||||
SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x and SX128x
|
||||
* [__Morse Code__](https://www.sigidwiki.com/wiki/Morse_Code_(CW)) using 2-FSK or AFSK for modules:
|
||||
SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x and SX128x
|
||||
* [__SSTV__](https://www.sigidwiki.com/wiki/SSTV) using 2-FSK or AFSK for modules:
|
||||
SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, RFM2x and Si443x
|
||||
* [__Hellschreiber__](https://www.sigidwiki.com/wiki/Hellschreiber) using 2-FSK or AFSK for modules:
|
||||
SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x and SX128x
|
||||
* [__APRS__](https://www.sigidwiki.com/wiki/APRS) using AFSK for modules:
|
||||
SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x and SX128x
|
||||
* [__POCSAG__](https://www.sigidwiki.com/wiki/POCSAG) using 2-FSK for modules:
|
||||
SX127x, RFM9x, RF69, SX1231, CC1101, nRF24L01, RFM2x and Si443x
|
||||
* [__LoRaWAN__](https://lora-alliance.org/) using LoRa for modules:
|
||||
SX127x, RFM9x, SX126x and SX128x
|
||||
* NOTE: LoRaWAN support is currently in beta, feedback via [Issues](https://github.com/jgromes/RadioLib/issues) and [Discussions](https://github.com/jgromes/RadioLib/discussions) is appreciated!
|
||||
|
||||
### Supported Arduino platforms:
|
||||
* __Arduino__
|
||||
* [__AVR__](https://github.com/arduino/ArduinoCore-avr) - Arduino Uno, Mega, Leonardo, Pro Mini, Nano etc.
|
||||
* [__mbed__](https://github.com/arduino/ArduinoCore-mbed) - Arduino Nano 33 BLE and Arduino Portenta H7
|
||||
* [__megaAVR__](https://github.com/arduino/ArduinoCore-megaavr) - Arduino Uno WiFi Rev.2 and Nano Every
|
||||
* [__SAM__](https://github.com/arduino/ArduinoCore-sam) - Arduino Due
|
||||
* [__SAMD__](https://github.com/arduino/ArduinoCore-samd) - Arduino Zero, MKR boards, M0 Pro etc.
|
||||
* [__Renesas__](https://github.com/arduino/ArduinoCore-renesas) - Arduino Uno R4
|
||||
|
||||
* __Adafruit__
|
||||
* [__SAMD__](https://github.com/adafruit/ArduinoCore-samd) - Adafruit Feather M0 and M4 boards (Feather, Metro, Gemma, Trinket etc.)
|
||||
* [__nRF52__](https://github.com/adafruit/Adafruit_nRF52_Arduino) - Adafruit Feather nRF528x, Bluefruit and CLUE
|
||||
|
||||
* __Espressif__
|
||||
* [__ESP32__](https://github.com/espressif/arduino-esp32) - ESP32-based boards
|
||||
* [__ESP8266__](https://github.com/esp8266/Arduino) - ESP8266-based boards
|
||||
|
||||
* __Intel__
|
||||
* [__Curie__](https://github.com/arduino/ArduinoCore-arc32) - Arduino 101
|
||||
|
||||
* __SparkFun__
|
||||
* [__Apollo3__](https://github.com/sparkfun/Arduino_Apollo3) - Sparkfun Artemis Redboard
|
||||
|
||||
* __ST Microelectronics__
|
||||
* [__STM32__ (official core)](https://github.com/stm32duino/Arduino_Core_STM32) - STM32 Nucleo, Discovery, Maple, BluePill, BlackPill etc.
|
||||
* [__STM32__ (unofficial core)](https://github.com/rogerclarkmelbourne/Arduino_STM32) - STM32F1 and STM32F4-based boards
|
||||
|
||||
* __MCUdude__
|
||||
* [__MegaCoreX__](https://github.com/MCUdude/MegaCoreX) - megaAVR-0 series (ATmega4809, ATmega3209 etc.)
|
||||
* [__MegaCore__](https://github.com/MCUdude/MegaCore) - AVR (ATmega1281, ATmega640 etc.)
|
||||
|
||||
* __Raspberry Pi__
|
||||
* [__RP2040__ (official core)](https://github.com/arduino/ArduinoCore-mbed) - Raspberry Pi Pico and Arduino Nano RP2040 Connect
|
||||
* [__RP2040__ (unofficial core)](https://github.com/earlephilhower/arduino-pico) - Raspberry Pi Pico/RP2040-based boards
|
||||
* [__Raspberry Pi__](https://github.com/me-no-dev/RasPiArduino) - Arduino framework for RaspberryPI
|
||||
|
||||
* __Heltec__
|
||||
* [__CubeCell__](https://github.com/HelTecAutomation/CubeCell-Arduino) - ASR650X series (CubeCell-Board, CubeCell-Capsule, CubeCell-Module etc.)
|
||||
|
||||
* __PJRC__
|
||||
* [__Teensy__](https://github.com/PaulStoffregen/cores) - Teensy 2.x, 3.x and 4.x boards
|
||||
|
||||
The list above is by no means exhaustive - RadioLib code is independent of the used platform! Compilation of all examples is tested for all platforms officially supported prior to releasing new version. In addition, RadioLib includes an internal hardware abstraction layer, which allows it to be easily ported even to non-Arduino environments.
|
5
lib/libesp32/RadioLib/SECURITY.md
Normal file
5
lib/libesp32/RadioLib/SECURITY.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
RadioLib is provided as-is without any warranty, and is not intended to be used in security-critical applications. However, if you discover a vulnerability within the library code, please report it to gromes.jan@gmail.com.
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
RadioLib AFSK External Radio example
|
||||
|
||||
This example shows how to use your Arduino
|
||||
as modulator for an external analogue FM radio.
|
||||
|
||||
The example sends APRS position reports with
|
||||
audio modulated as AFSK at 1200 baud using
|
||||
Bell 202 tones. However, any other AFSK
|
||||
protocol (RTTY, SSTV, etc.) may be used as well.
|
||||
|
||||
DO NOT transmit in APRS bands unless
|
||||
you have a ham radio license!
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// create a dummy radio module
|
||||
ExternalRadio radio;
|
||||
|
||||
// create AFSK client instance using the external radio
|
||||
// pin 5 is connected to the radio sound input
|
||||
AFSKClient audio(&radio, 5);
|
||||
|
||||
// create AX.25 client instance using the AFSK instance
|
||||
AX25Client ax25(&audio);
|
||||
|
||||
// create APRS client instance using the AX.25 client
|
||||
APRSClient aprs(&ax25);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize AX.25 client
|
||||
Serial.print(F("[AX.25] Initializing ... "));
|
||||
// source station callsign: "N7LEM"
|
||||
// source station SSID: 0
|
||||
// preamble length: 8 bytes
|
||||
int16_t state = ax25.begin("N7LEM");
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize APRS client
|
||||
Serial.print(F("[APRS] Initializing ... "));
|
||||
// symbol: '>' (car)
|
||||
state = aprs.begin('>');
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[APRS] Sending position ... "));
|
||||
|
||||
// send a location without message or timestamp
|
||||
int state = aprs.sendPosition("N0CALL", 0, "4911.67N", "01635.96E");
|
||||
delay(500);
|
||||
|
||||
// send a location with message and without timestamp
|
||||
state |= aprs.sendPosition("N0CALL", 0, "4911.67N", "01635.96E", "I'm here!");
|
||||
delay(500);
|
||||
|
||||
// send a location with message and timestamp
|
||||
state |= aprs.sendPosition("N0CALL", 0, "4911.67N", "01635.96E", "I'm here!", "093045z");
|
||||
delay(500);
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
// wait one minute before transmitting again
|
||||
delay(60000);
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
RadioLib AFSK Imperial March Example
|
||||
|
||||
This example shows how to EXECUTE ORDER 66
|
||||
|
||||
Other modules that can be used for AFSK:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- Si443x/RFM2x
|
||||
- SX126x/LLCC68 (only devices without TCXO!)
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// include the melody
|
||||
#include "melody.h"
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// create AFSK client instance using the FSK module
|
||||
// this requires connection to the module direct
|
||||
// input pin, here connected to Arduino pin 5
|
||||
// SX127x/RFM9x: DIO2
|
||||
// RF69: DIO2
|
||||
// SX1231: DIO2
|
||||
// CC1101: GDO2
|
||||
// Si443x/RFM2x: GPIO
|
||||
// SX126x/LLCC68: DIO2
|
||||
AFSKClient audio(&radio, 5);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules for AFSK
|
||||
// (RF69, CC1101,, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize AFSK client
|
||||
Serial.print(F("[AFSK] Initializing ... "));
|
||||
state = audio.begin();
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[AFSK] Executing Order 66 ... "));
|
||||
|
||||
// calculate whole note duration
|
||||
int wholenote = (60000 * 4) / 120;
|
||||
|
||||
// iterate over the melody
|
||||
for(unsigned int note = 0; note < sizeof(melody) / sizeof(melody[0]); note += 2) {
|
||||
// calculate the duration of each note
|
||||
int noteDuration = 0;
|
||||
int divider = melody[note + 1];
|
||||
if(divider > 0) {
|
||||
// regular note, just proceed
|
||||
noteDuration = wholenote / divider;
|
||||
} else if(divider < 0) {
|
||||
// dotted notes are represented with negative durations!!
|
||||
noteDuration = wholenote / abs(divider);
|
||||
noteDuration *= 1.5; // increases the duration in half for dotted notes
|
||||
}
|
||||
|
||||
// we only play the note for 90% of the duration, leaving 10% as a pause
|
||||
audio.tone(melody[note]);
|
||||
delay(noteDuration*0.9);
|
||||
audio.noTone();
|
||||
delay(noteDuration*0.1);
|
||||
}
|
||||
|
||||
Serial.println(F("done!"));
|
||||
|
||||
// wait for a second
|
||||
delay(1000);
|
||||
}
|
128
lib/libesp32/RadioLib/examples/AFSK/AFSK_Imperial_March/melody.h
Normal file
128
lib/libesp32/RadioLib/examples/AFSK/AFSK_Imperial_March/melody.h
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
Note definitions, melody and melody-related functions
|
||||
adapted from https://github.com/robsoncouto/arduino-songs
|
||||
by Robson Couto, 2019
|
||||
*/
|
||||
|
||||
#define NOTE_B0 31
|
||||
#define NOTE_C1 33
|
||||
#define NOTE_CS1 35
|
||||
#define NOTE_D1 37
|
||||
#define NOTE_DS1 39
|
||||
#define NOTE_E1 41
|
||||
#define NOTE_F1 44
|
||||
#define NOTE_FS1 46
|
||||
#define NOTE_G1 49
|
||||
#define NOTE_GS1 52
|
||||
#define NOTE_A1 55
|
||||
#define NOTE_AS1 58
|
||||
#define NOTE_B1 62
|
||||
#define NOTE_C2 65
|
||||
#define NOTE_CS2 69
|
||||
#define NOTE_D2 73
|
||||
#define NOTE_DS2 78
|
||||
#define NOTE_E2 82
|
||||
#define NOTE_F2 87
|
||||
#define NOTE_FS2 93
|
||||
#define NOTE_G2 98
|
||||
#define NOTE_GS2 104
|
||||
#define NOTE_A2 110
|
||||
#define NOTE_AS2 117
|
||||
#define NOTE_B2 123
|
||||
#define NOTE_C3 131
|
||||
#define NOTE_CS3 139
|
||||
#define NOTE_D3 147
|
||||
#define NOTE_DS3 156
|
||||
#define NOTE_E3 165
|
||||
#define NOTE_F3 175
|
||||
#define NOTE_FS3 185
|
||||
#define NOTE_G3 196
|
||||
#define NOTE_GS3 208
|
||||
#define NOTE_A3 220
|
||||
#define NOTE_AS3 233
|
||||
#define NOTE_B3 247
|
||||
#define NOTE_C4 262
|
||||
#define NOTE_CS4 277
|
||||
#define NOTE_D4 294
|
||||
#define NOTE_DS4 311
|
||||
#define NOTE_E4 330
|
||||
#define NOTE_F4 349
|
||||
#define NOTE_FS4 370
|
||||
#define NOTE_G4 392
|
||||
#define NOTE_GS4 415
|
||||
#define NOTE_A4 440
|
||||
#define NOTE_AS4 466
|
||||
#define NOTE_B4 494
|
||||
#define NOTE_C5 523
|
||||
#define NOTE_CS5 554
|
||||
#define NOTE_D5 587
|
||||
#define NOTE_DS5 622
|
||||
#define NOTE_E5 659
|
||||
#define NOTE_F5 698
|
||||
#define NOTE_FS5 740
|
||||
#define NOTE_G5 784
|
||||
#define NOTE_GS5 831
|
||||
#define NOTE_A5 880
|
||||
#define NOTE_AS5 932
|
||||
#define NOTE_B5 988
|
||||
#define NOTE_C6 1047
|
||||
#define NOTE_CS6 1109
|
||||
#define NOTE_D6 1175
|
||||
#define NOTE_DS6 1245
|
||||
#define NOTE_E6 1319
|
||||
#define NOTE_F6 1397
|
||||
#define NOTE_FS6 1480
|
||||
#define NOTE_G6 1568
|
||||
#define NOTE_GS6 1661
|
||||
#define NOTE_A6 1760
|
||||
#define NOTE_AS6 1865
|
||||
#define NOTE_B6 1976
|
||||
#define NOTE_C7 2093
|
||||
#define NOTE_CS7 2217
|
||||
#define NOTE_D7 2349
|
||||
#define NOTE_DS7 2489
|
||||
#define NOTE_E7 2637
|
||||
#define NOTE_F7 2794
|
||||
#define NOTE_FS7 2960
|
||||
#define NOTE_G7 3136
|
||||
#define NOTE_GS7 3322
|
||||
#define NOTE_A7 3520
|
||||
#define NOTE_AS7 3729
|
||||
#define NOTE_B7 3951
|
||||
#define NOTE_C8 4186
|
||||
#define NOTE_CS8 4435
|
||||
#define NOTE_D8 4699
|
||||
#define NOTE_DS8 4978
|
||||
#define REST 0
|
||||
|
||||
// notes of the melody followed by the duration.
|
||||
// a 4 means a quarter note, 8 an eighteenth , 16 sixteenth, so on
|
||||
// !!negative numbers are used to represent dotted notes,
|
||||
// so -4 means a dotted quarter note, that is, a quarter plus an eighteenth!!
|
||||
int melody[] = {
|
||||
|
||||
// Darth Vader theme (Imperial March) - Star wars
|
||||
// Score available at https://musescore.com/user/202909/scores/1141521
|
||||
// The tenor saxophone part was used
|
||||
|
||||
NOTE_A4,-4, NOTE_A4,-4, NOTE_A4,16, NOTE_A4,16, NOTE_A4,16, NOTE_A4,16, NOTE_F4,8, REST,8,
|
||||
NOTE_A4,-4, NOTE_A4,-4, NOTE_A4,16, NOTE_A4,16, NOTE_A4,16, NOTE_A4,16, NOTE_F4,8, REST,8,
|
||||
NOTE_A4,4, NOTE_A4,4, NOTE_A4,4, NOTE_F4,-8, NOTE_C5,16,
|
||||
|
||||
NOTE_A4,4, NOTE_F4,-8, NOTE_C5,16, NOTE_A4,2,//4
|
||||
NOTE_E5,4, NOTE_E5,4, NOTE_E5,4, NOTE_F5,-8, NOTE_C5,16,
|
||||
NOTE_A4,4, NOTE_F4,-8, NOTE_C5,16, NOTE_A4,2,
|
||||
|
||||
NOTE_A5,4, NOTE_A4,-8, NOTE_A4,16, NOTE_A5,4, NOTE_GS5,-8, NOTE_G5,16, //7
|
||||
NOTE_DS5,16, NOTE_D5,16, NOTE_DS5,8, REST,8, NOTE_A4,8, NOTE_DS5,4, NOTE_D5,-8, NOTE_CS5,16,
|
||||
|
||||
NOTE_C5,16, NOTE_B4,16, NOTE_C5,16, REST,8, NOTE_F4,8, NOTE_GS4,4, NOTE_F4,-8, NOTE_A4,-16,//9
|
||||
NOTE_C5,4, NOTE_A4,-8, NOTE_C5,16, NOTE_E5,2,
|
||||
|
||||
NOTE_A5,4, NOTE_A4,-8, NOTE_A4,16, NOTE_A5,4, NOTE_GS5,-8, NOTE_G5,16, //7
|
||||
NOTE_DS5,16, NOTE_D5,16, NOTE_DS5,8, REST,8, NOTE_A4,8, NOTE_DS5,4, NOTE_D5,-8, NOTE_CS5,16,
|
||||
|
||||
NOTE_C5,16, NOTE_B4,16, NOTE_C5,16, REST,8, NOTE_F4,8, NOTE_GS4,4, NOTE_F4,-8, NOTE_A4,-16,//9
|
||||
NOTE_A4,4, NOTE_F4,-8, NOTE_C5,16, NOTE_A4,2,
|
||||
|
||||
};
|
92
lib/libesp32/RadioLib/examples/AFSK/AFSK_Tone/AFSK_Tone.ino
Normal file
92
lib/libesp32/RadioLib/examples/AFSK/AFSK_Tone/AFSK_Tone.ino
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
RadioLib AFSK Example
|
||||
|
||||
This example shows hot to send audio FSK tones
|
||||
using SX1278's FSK modem.
|
||||
|
||||
Other modules that can be used for AFSK:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- Si443x/RFM2x
|
||||
- SX126x/LLCC68 (only devices without TCXO!)
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// create AFSK client instance using the FSK module
|
||||
// this requires connection to the module direct
|
||||
// input pin, here connected to Arduino pin 5
|
||||
// SX127x/RFM9x: DIO2
|
||||
// RF69: DIO2
|
||||
// SX1231: DIO2
|
||||
// CC1101: GDO2
|
||||
// Si443x/RFM2x: GPIO
|
||||
// SX126x/LLCC68: DIO2
|
||||
AFSKClient audio(&radio, 5);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules for AFSK
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize AFSK client
|
||||
Serial.print(F("[AFSK] Initializing ... "));
|
||||
state = audio.begin();
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// AFSKClient can be used to transmit tones,
|
||||
// same as Arduino tone() function
|
||||
|
||||
// 400 Hz tone
|
||||
Serial.print(F("[AFSK] 400 Hz tone ... "));
|
||||
audio.tone(400);
|
||||
delay(1000);
|
||||
|
||||
// silence
|
||||
Serial.println(F("done!"));
|
||||
audio.noTone();
|
||||
delay(1000);
|
||||
|
||||
// AFSKClient can also be used to transmit HAM-friendly
|
||||
// RTTY, Morse code, Hellschreiber, SSTV and AX.25.
|
||||
// Details on how to use AFSK are in the example
|
||||
// folders for each of the above modes.
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
RadioLib AM-modulated AFSK Example
|
||||
|
||||
This example shows hot to send AM-modulated
|
||||
audio FSK tones using SX1278's OOK modem.
|
||||
|
||||
Other modules that can be used for AFSK:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
SX1278 radio = new Module(10, 2, 9);
|
||||
|
||||
// create AFSK client instance using the FSK module
|
||||
// this requires connection to the module direct
|
||||
// input pin, here connected to Arduino pin 5
|
||||
// SX127x/RFM9x: DIO2
|
||||
// RF69: DIO2
|
||||
// SX1231: DIO2
|
||||
// CC1101: GDO2
|
||||
AFSKClient audio(&radio, 5);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules for AFSK
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize AFSK client
|
||||
Serial.print(F("[AFSK] Initializing ... "));
|
||||
state = audio.begin();
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// after that, set mode to OOK
|
||||
Serial.print(F("[SX1278] Switching to OOK ... "));
|
||||
state = radio.setOOK(true);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// AFSKClient can be used to transmit tones,
|
||||
// same as Arduino tone() function
|
||||
|
||||
// 400 Hz tone
|
||||
Serial.print(F("[AFSK] 400 Hz tone ... "));
|
||||
audio.tone(400);
|
||||
delay(1000);
|
||||
|
||||
// silence
|
||||
Serial.println(F("done!"));
|
||||
audio.noTone();
|
||||
delay(1000);
|
||||
|
||||
// AFSKClient can also be used to transmit HAM-friendly
|
||||
// RTTY, Morse code, Hellschreiber, SSTV and AX.25.
|
||||
// Details on how to use AFSK are in the example
|
||||
// folders for each of the above modes.
|
||||
|
||||
// CAUTION: Unlike standard AFSK, the result when using OOK
|
||||
// must be demodulated as AM!
|
||||
}
|
119
lib/libesp32/RadioLib/examples/APRS/APRS_MicE/APRS_MicE.ino
Normal file
119
lib/libesp32/RadioLib/examples/APRS/APRS_MicE/APRS_MicE.ino
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
RadioLib APRS Mic-E Example
|
||||
|
||||
This example sends APRS position reports
|
||||
encoded in the Mic-E format using SX1278's
|
||||
FSK modem. The data is modulated as AFSK
|
||||
at 1200 baud using Bell 202 tones.
|
||||
|
||||
DO NOT transmit in APRS bands unless
|
||||
you have a ham radio license!
|
||||
|
||||
Other modules that can be used for APRS:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- nRF24
|
||||
- Si443x/RFM2x
|
||||
- SX126x/LLCC68 (only devices without TCXO!)
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create AFSK client instance using the FSK module
|
||||
// this requires connection to the module direct
|
||||
// input pin, here connected to Arduino pin 5
|
||||
// SX127x/RFM9x: DIO2
|
||||
// RF69: DIO2
|
||||
// SX1231: DIO2
|
||||
// CC1101: GDO2
|
||||
// Si443x/RFM2x: GPIO
|
||||
// SX126x/LLCC68: DIO2
|
||||
AFSKClient audio(&radio, 5);
|
||||
|
||||
// create AX.25 client instance using the AFSK instance
|
||||
AX25Client ax25(&audio);
|
||||
|
||||
// create APRS client instance using the AX.25 client
|
||||
APRSClient aprs(&ax25);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278
|
||||
// NOTE: moved to ISM band on purpose
|
||||
// DO NOT transmit in APRS bands without ham radio license!
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules for AX.25
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize AX.25 client
|
||||
Serial.print(F("[AX.25] Initializing ... "));
|
||||
// source station callsign: "N7LEM"
|
||||
// source station SSID: 0
|
||||
// preamble length: 8 bytes
|
||||
state = ax25.begin("N7LEM");
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize APRS client
|
||||
Serial.print(F("[APRS] Initializing ... "));
|
||||
// symbol: '>' (car)
|
||||
state = aprs.begin('>');
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[APRS] Sending Mic-E position ... "));
|
||||
int state = aprs.sendMicE(49.1945, 16.6000, 120, 10, RADIOLIB_APRS_MIC_E_TYPE_EN_ROUTE);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
// wait one minute before transmitting again
|
||||
delay(60000);
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
/*
|
||||
RadioLib APRS Position Example
|
||||
|
||||
This example sends APRS position reports
|
||||
using SX1278's FSK modem. The data is
|
||||
modulated as AFSK at 1200 baud using Bell
|
||||
202 tones.
|
||||
|
||||
DO NOT transmit in APRS bands unless
|
||||
you have a ham radio license!
|
||||
|
||||
Other modules that can be used for APRS:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- nRF24
|
||||
- Si443x/RFM2x
|
||||
- SX126x/LLCC68 (only devices without TCXO!)
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create AFSK client instance using the FSK module
|
||||
// this requires connection to the module direct
|
||||
// input pin, here connected to Arduino pin 5
|
||||
// SX127x/RFM9x: DIO2
|
||||
// RF69: DIO2
|
||||
// SX1231: DIO2
|
||||
// CC1101: GDO2
|
||||
// Si443x/RFM2x: GPIO
|
||||
// SX126x/LLCC68: DIO2
|
||||
AFSKClient audio(&radio, 5);
|
||||
|
||||
// create AX.25 client instance using the AFSK instance
|
||||
AX25Client ax25(&audio);
|
||||
|
||||
// create APRS client instance using the AX.25 client
|
||||
APRSClient aprs(&ax25);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278
|
||||
// NOTE: moved to ISM band on purpose
|
||||
// DO NOT transmit in APRS bands without ham radio license!
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK(434.0);
|
||||
|
||||
// when using one of the non-LoRa modules for AX.25
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize AX.25 client
|
||||
Serial.print(F("[AX.25] Initializing ... "));
|
||||
// source station callsign: "N7LEM"
|
||||
// source station SSID: 0
|
||||
// preamble length: 8 bytes
|
||||
state = ax25.begin("N7LEM");
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize APRS client
|
||||
Serial.print(F("[APRS] Initializing ... "));
|
||||
// symbol: '>' (car)
|
||||
state = aprs.begin('>');
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[APRS] Sending position ... "));
|
||||
|
||||
// send a location without message or timestamp
|
||||
int state = aprs.sendPosition("N0CALL", 0, "4911.67N", "01635.96E");
|
||||
delay(500);
|
||||
|
||||
// send a location with message and without timestamp
|
||||
state |= aprs.sendPosition("N0CALL", 0, "4911.67N", "01635.96E", "I'm here!");
|
||||
delay(500);
|
||||
|
||||
// send a location with message and timestamp
|
||||
state |= aprs.sendPosition("N0CALL", 0, "4911.67N", "01635.96E", "I'm here!", "093045z");
|
||||
delay(500);
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
// wait one minute before transmitting again
|
||||
delay(60000);
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
RadioLib APRS Position over LoRa Example
|
||||
|
||||
This example sends APRS position reports
|
||||
using SX1278's LoRa modem.
|
||||
|
||||
Other modules that can be used for APRS:
|
||||
- SX127x/RFM9x
|
||||
- SX126x/LLCC68
|
||||
- SX128x
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create APRS client instance using the LoRa radio
|
||||
APRSClient aprs(&radio);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with the settings necessary for LoRa iGates
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
// frequency: 433.775 MHz
|
||||
// bandwidth: 125 kHz
|
||||
// spreading factor: 12
|
||||
// coding rate: 4/5
|
||||
int state = radio.begin(433.775, 125, 12, 5);
|
||||
|
||||
// when using one of the non-LoRa modules for AX.25
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize APRS client
|
||||
Serial.print(F("[APRS] Initializing ... "));
|
||||
// symbol: '>' (car)
|
||||
// callsign "N7LEM"
|
||||
// SSID 1
|
||||
state = aprs.begin('>', "N7LEM", 1);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[APRS] Sending position ... "));
|
||||
|
||||
// send a location with message and timestamp
|
||||
// SSID is set to 1, as APRS over LoRa uses WIDE1-1 path by default
|
||||
int state = aprs.sendPosition("GPS", 1, "4911.67N", "01635.96E", "I'm here!", "093045z");
|
||||
delay(500);
|
||||
|
||||
// you can also send Mic-E encoded messages
|
||||
state |= state = aprs.sendMicE(49.1945, 16.6000, 120, 10, RADIOLIB_APRS_MIC_E_TYPE_EN_ROUTE);
|
||||
delay(500);
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
// wait one minute before transmitting again
|
||||
delay(60000);
|
||||
}
|
174
lib/libesp32/RadioLib/examples/AX25/AX25_Frames/AX25_Frames.ino
Normal file
174
lib/libesp32/RadioLib/examples/AX25/AX25_Frames/AX25_Frames.ino
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
RadioLib AX.25 Frame Example
|
||||
|
||||
This example shows how to send various
|
||||
AX.25 frames using SX1278's FSK modem.
|
||||
|
||||
Other modules that can be used for AX.25:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- SX126x
|
||||
- nRF24
|
||||
- Si443x/RFM2x
|
||||
|
||||
Using raw AX.25 frames requires some
|
||||
knowledge of the protocol, refer to
|
||||
AX25_Transmit for basic operation.
|
||||
Frames shown in this example are not
|
||||
exhaustive; all possible AX.25 frames
|
||||
should be supported.
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create AX.25 client instance using the FSK module
|
||||
AX25Client ax25(&radio);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
// carrier frequency: 434.0 MHz
|
||||
// bit rate: 1.2 kbps (1200 baud 2-FSK AX.25)
|
||||
// frequency deviation: 0.5 kHz (1200 baud 2-FSK AX.25)
|
||||
int state = radio.beginFSK(434.0, 1.2, 0.5);
|
||||
|
||||
// when using one of the non-LoRa modules for AX.25
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize AX.25 client
|
||||
Serial.print(F("[AX.25] Initializing ... "));
|
||||
// source station callsign: "N7LEM"
|
||||
// source station SSID: 0
|
||||
// preamble length: 8 bytes
|
||||
state = ax25.begin("N7LEM");
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// create AX.25 Unnumbered Information frame
|
||||
// destination station callsign: "NJ7P"
|
||||
// destination station SSID: 0
|
||||
// source station callsign: "N7LEM"
|
||||
// source station SSID: 0
|
||||
// control field: UI, P/F not used, unnumbered frame
|
||||
// protocol identifier: no layer 3 protocol implemented
|
||||
// information field: "Hello World!"
|
||||
AX25Frame frameUI("NJ7P", 0, "N7LEM", 0, RADIOLIB_AX25_CONTROL_U_UNNUMBERED_INFORMATION |
|
||||
RADIOLIB_AX25_CONTROL_POLL_FINAL_DISABLED | RADIOLIB_AX25_CONTROL_UNNUMBERED_FRAME,
|
||||
RADIOLIB_AX25_PID_NO_LAYER_3, "Hello World (unnumbered)!");
|
||||
|
||||
// send the frame
|
||||
Serial.print(F("[AX.25] Sending UI frame ... "));
|
||||
int state = ax25.sendFrame(&frameUI);
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
// the packet was successfully transmitted
|
||||
Serial.println(F("success!"));
|
||||
|
||||
} else {
|
||||
// some error occurred
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
|
||||
}
|
||||
|
||||
delay(1000);
|
||||
|
||||
// create AX.25 Receive Ready frame
|
||||
// destination station callsign: "NJ7P"
|
||||
// destination station SSID: 0
|
||||
// source station callsign: "N7LEM"
|
||||
// source station SSID: 0
|
||||
// control field: RR, P/F not used, supervisory frame
|
||||
AX25Frame frameRR("NJ7P", 0, "N7LEM", 0, RADIOLIB_AX25_CONTROL_S_RECEIVE_READY |
|
||||
RADIOLIB_AX25_CONTROL_POLL_FINAL_DISABLED | RADIOLIB_AX25_CONTROL_SUPERVISORY_FRAME);
|
||||
|
||||
// set receive sequence number (0 - 7)
|
||||
frameRR.setRecvSequence(0);
|
||||
|
||||
// send the frame
|
||||
Serial.print(F("[AX.25] Sending RR frame ... "));
|
||||
state = ax25.sendFrame(&frameRR);
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
// the packet was successfully transmitted
|
||||
Serial.println(F("success!"));
|
||||
|
||||
} else {
|
||||
// some error occurred
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
|
||||
}
|
||||
|
||||
delay(1000);
|
||||
|
||||
// create AX.25 Information frame
|
||||
// destination station callsign: "NJ7P"
|
||||
// destination station SSID: 0
|
||||
// source station callsign: "N7LEM"
|
||||
// source station SSID: 0
|
||||
// control field: P/F not used, information frame
|
||||
// protocol identifier: no layer 3 protocol implemented
|
||||
// information field: "Hello World (numbered)!"
|
||||
AX25Frame frameI("NJ7P", 0, "N7LEM", 0, RADIOLIB_AX25_CONTROL_POLL_FINAL_DISABLED |
|
||||
RADIOLIB_AX25_CONTROL_INFORMATION_FRAME, RADIOLIB_AX25_PID_NO_LAYER_3,
|
||||
"Hello World (numbered)!");
|
||||
|
||||
// set receive sequence number (0 - 7)
|
||||
frameI.setRecvSequence(0);
|
||||
|
||||
// set send sequence number (0 - 7)
|
||||
frameI.setSendSequence(0);
|
||||
|
||||
// send the frame
|
||||
Serial.print(F("[AX.25] Sending I frame ... "));
|
||||
state = ax25.sendFrame(&frameI);
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
// the packet was successfully transmitted
|
||||
Serial.println(F("success!"));
|
||||
|
||||
} else {
|
||||
// some error occurred
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
|
||||
}
|
||||
|
||||
delay(1000);
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
RadioLib AX.25 Transmit Example
|
||||
|
||||
This example sends AX.25 messages using
|
||||
SX1278's FSK modem.
|
||||
|
||||
Other modules that can be used for AX.25:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- SX126x
|
||||
- nRF24
|
||||
- Si443x/RFM2x
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create AX.25 client instance using the FSK module
|
||||
AX25Client ax25(&radio);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
// carrier frequency: 434.0 MHz
|
||||
// bit rate: 1.2 kbps (1200 baud 2-FSK AX.25)
|
||||
int state = radio.beginFSK(434.0, 1.2);
|
||||
|
||||
// when using one of the non-LoRa modules for AX.25
|
||||
// (RF69, CC1101,, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize AX.25 client
|
||||
Serial.print(F("[AX.25] Initializing ... "));
|
||||
// source station callsign: "N7LEM"
|
||||
// source station SSID: 0
|
||||
// preamble length: 8 bytes
|
||||
state = ax25.begin("N7LEM");
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// send AX.25 unnumbered information frame
|
||||
Serial.print(F("[AX.25] Sending UI frame ... "));
|
||||
// destination station callsign: "NJ7P"
|
||||
// destination station SSID: 0
|
||||
int state = ax25.transmit("Hello World!", "NJ7P");
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
// the packet was successfully transmitted
|
||||
Serial.println(F("success!"));
|
||||
|
||||
} else {
|
||||
// some error occurred
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
|
||||
}
|
||||
|
||||
delay(1000);
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
/*
|
||||
RadioLib AX.25 Transmit AFSK Example
|
||||
|
||||
This example sends AX.25 messages using
|
||||
SX1278's FSK modem. The data is modulated
|
||||
as AFSK at 1200 baud using Bell 202 tones.
|
||||
|
||||
Other modules that can be used for AX.25
|
||||
with AFSK modulation:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- nRF24
|
||||
- Si443x/RFM2x
|
||||
- SX126x/LLCC68 (only devices without TCXO!)
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create AFSK client instance using the FSK module
|
||||
// this requires connection to the module direct
|
||||
// input pin, here connected to Arduino pin 5
|
||||
// SX127x/RFM9x: DIO2
|
||||
// RF69: DIO2
|
||||
// SX1231: DIO2
|
||||
// CC1101: GDO2
|
||||
// Si443x/RFM2x: GPIO
|
||||
// SX126x/LLCC68: DIO2
|
||||
AFSKClient audio(&radio, 5);
|
||||
|
||||
// create AX.25 client instance using the AFSK instance
|
||||
AX25Client ax25(&audio);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules for AX.25
|
||||
// (RF69, CC1101,, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize AX.25 client
|
||||
Serial.print(F("[AX.25] Initializing ... "));
|
||||
// source station callsign: "N7LEM"
|
||||
// source station SSID: 0
|
||||
// preamble length: 8 bytes
|
||||
state = ax25.begin("N7LEM");
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// Sometimes, it may be required to adjust audio
|
||||
// frequencies to match the expected 1200/2200 Hz tones.
|
||||
// The following method will offset mark frequency by
|
||||
// 100 Hz up and space frequency by 100 Hz down
|
||||
/*
|
||||
Serial.print(F("[AX.25] Setting correction ... "));
|
||||
state = ax25.setCorrection(100, -100);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// send AX.25 unnumbered information frame
|
||||
Serial.print(F("[AX.25] Sending UI frame ... "));
|
||||
// destination station callsign: "NJ7P"
|
||||
// destination station SSID: 0
|
||||
int state = ax25.transmit("Hello World!", "NJ7P");
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
// the packet was successfully transmitted
|
||||
Serial.println(F("success!"));
|
||||
|
||||
} else {
|
||||
// some error occurred
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
|
||||
}
|
||||
|
||||
delay(1000);
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
RadioLib Bell Modem Transmit Example
|
||||
|
||||
This example shows how to transmit binary data
|
||||
using audio Bell 202 tones.
|
||||
|
||||
Other implemented Bell modems
|
||||
- Bell 101
|
||||
- Bell 103
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// create Bell modem instance using the FSK module
|
||||
// this requires connection to the module direct
|
||||
// input pin, here connected to Arduino pin 5
|
||||
// SX127x/RFM9x: DIO2
|
||||
// RF69: DIO2
|
||||
// SX1231: DIO2
|
||||
// CC1101: GDO2
|
||||
// Si443x/RFM2x: GPIO
|
||||
// SX126x/LLCC68: DIO2 (only devices without TCXO!)
|
||||
BellClient bell(&radio, 5);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules for AFSK
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize Bell 202 modem
|
||||
Serial.print(F("[Bell 202] Initializing ... "));
|
||||
state = bell.begin(Bell202);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[Bell 202] Sending data ... "));
|
||||
|
||||
// send out idle condition for 500 ms
|
||||
bell.idle();
|
||||
delay(500);
|
||||
|
||||
// BellClient supports all methods of the Serial class
|
||||
|
||||
// Arduino String class
|
||||
String aStr = "Arduino String";
|
||||
bell.println(aStr);
|
||||
|
||||
// character array (C-String)
|
||||
bell.println("C-String");
|
||||
|
||||
// string saved in flash
|
||||
bell.println(F("Flash String"));
|
||||
|
||||
// character
|
||||
bell.println('c');
|
||||
|
||||
// byte
|
||||
// formatting DEC/HEX/OCT/BIN is supported for
|
||||
// any integer type (byte/int/long)
|
||||
bell.println(255, HEX);
|
||||
|
||||
// integer number
|
||||
int i = 1000;
|
||||
bell.println(i);
|
||||
|
||||
// floating point number
|
||||
float f = -3.1415;
|
||||
bell.println(f, 3);
|
||||
|
||||
// ITA2-encoded string
|
||||
ITA2String str("HELLO WORLD!");
|
||||
bell.print(str);
|
||||
|
||||
// turn the transmitter off
|
||||
bell.standby();
|
||||
|
||||
Serial.println(F("done!"));
|
||||
|
||||
// wait for a second before transmitting again
|
||||
delay(1000);
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
RadioLib CC1101 Receive with Address Example
|
||||
|
||||
This example receives packets using CC1101 FSK radio
|
||||
module. Packets can have 1-byte address of the
|
||||
destination node. After setting node address, this node
|
||||
will automatically filter out any packets that do not
|
||||
contain either node address or broadcast addresses.
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration#cc1101
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// CC1101 has the following connections:
|
||||
// CS pin: 10
|
||||
// GDO0 pin: 2
|
||||
// RST pin: unused
|
||||
// GDO2 pin: 3 (optional)
|
||||
CC1101 radio = new Module(10, 2, RADIOLIB_NC, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//CC1101 radio = RadioShield.ModuleA;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize CC1101 with default settings
|
||||
Serial.print(F("[CC1101] Initializing ... "));
|
||||
int state = radio.begin();
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while (true);
|
||||
}
|
||||
|
||||
// set node address
|
||||
// NOTE: Calling this method will automatically enable
|
||||
// address filtering. CC1101 also allows to set
|
||||
// number of broadcast address (0/1/2).
|
||||
// The following sets one broadcast address 0x00.
|
||||
// When setting two broadcast addresses, 0x00 and
|
||||
// 0xFF will be used.
|
||||
Serial.print(F("[CC1101] Setting node address ... "));
|
||||
state = radio.setNodeAddress(0x01, 1);
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while (true);
|
||||
}
|
||||
|
||||
// address filtering can also be disabled
|
||||
// NOTE: Calling this method will also erase previously
|
||||
// set node address
|
||||
/*
|
||||
Serial.print(F("[CC1101] Disabling address filtering ... "));
|
||||
state == radio.disableAddressFiltering();
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[CC1101] Waiting for incoming transmission ... "));
|
||||
|
||||
// you can receive data as an Arduino String
|
||||
String str;
|
||||
int state = radio.receive(str);
|
||||
|
||||
// you can also receive data as byte array
|
||||
/*
|
||||
byte byteArr[8];
|
||||
int state = radio.receive(byteArr, 8);
|
||||
*/
|
||||
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
// packet was successfully received
|
||||
Serial.println(F("success!"));
|
||||
|
||||
// print the data of the packet
|
||||
Serial.print(F("[CC1101] Data:\t\t"));
|
||||
Serial.println(str);
|
||||
|
||||
// print RSSI (Received Signal Strength Indicator)
|
||||
// of the last received packet
|
||||
Serial.print(F("[CC1101] RSSI:\t\t"));
|
||||
Serial.print(radio.getRSSI());
|
||||
Serial.println(F(" dBm"));
|
||||
|
||||
// print LQI (Link Quality Indicator)
|
||||
// of the last received packet, lower is better
|
||||
Serial.print(F("[CC1101] LQI:\t\t"));
|
||||
Serial.println(radio.getLQI());
|
||||
|
||||
} else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
|
||||
// packet was received, but is malformed
|
||||
Serial.println(F("CRC error!"));
|
||||
|
||||
} else {
|
||||
// some other error occurred
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
RadioLib CC1101 Blocking Receive Example
|
||||
|
||||
This example receives packets using CC1101 FSK radio module.
|
||||
To successfully receive data, the following settings have to be the same
|
||||
on both transmitter and receiver:
|
||||
- carrier frequency
|
||||
- bit rate
|
||||
- frequency deviation
|
||||
- sync word
|
||||
|
||||
Using blocking receive is not recommended, as it will lead
|
||||
to significant amount of timeouts, inefficient use of processor
|
||||
time and can some miss packets!
|
||||
Instead, interrupt receive is recommended.
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration#cc1101
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// CC1101 has the following connections:
|
||||
// CS pin: 10
|
||||
// GDO0 pin: 2
|
||||
// RST pin: unused
|
||||
// GDO2 pin: 3 (optional)
|
||||
CC1101 radio = new Module(10, 2, RADIOLIB_NC, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//CC1101 radio = RadioShield.ModuleA;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize CC1101 with default settings
|
||||
Serial.print(F("[CC1101] Initializing ... "));
|
||||
int state = radio.begin();
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while (true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[CC1101] Waiting for incoming transmission ... "));
|
||||
|
||||
// you can receive data as an Arduino String
|
||||
String str;
|
||||
int state = radio.receive(str);
|
||||
|
||||
// you can also receive data as byte array
|
||||
/*
|
||||
byte byteArr[8];
|
||||
int state = radio.receive(byteArr, 8);
|
||||
*/
|
||||
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
// packet was successfully received
|
||||
Serial.println(F("success!"));
|
||||
|
||||
// print the data of the packet
|
||||
Serial.print(F("[CC1101] Data:\t\t"));
|
||||
Serial.println(str);
|
||||
|
||||
// print RSSI (Received Signal Strength Indicator)
|
||||
// of the last received packet
|
||||
Serial.print(F("[CC1101] RSSI:\t\t"));
|
||||
Serial.print(radio.getRSSI());
|
||||
Serial.println(F(" dBm"));
|
||||
|
||||
// print LQI (Link Quality Indicator)
|
||||
// of the last received packet, lower is better
|
||||
Serial.print(F("[CC1101] LQI:\t\t"));
|
||||
Serial.println(radio.getLQI());
|
||||
|
||||
} else if (state == RADIOLIB_ERR_RX_TIMEOUT) {
|
||||
// timeout occurred while waiting for a packet
|
||||
Serial.println(F("timeout!"));
|
||||
|
||||
} else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
|
||||
// packet was received, but is malformed
|
||||
Serial.println(F("CRC error!"));
|
||||
|
||||
} else {
|
||||
// some other error occurred
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
/*
|
||||
RadioLib CC1101 Receive with Interrupts Example
|
||||
|
||||
This example listens for FSK transmissions and tries to
|
||||
receive them. Once a packet is received, an interrupt is
|
||||
triggered.
|
||||
|
||||
To successfully receive data, the following settings have to be the same
|
||||
on both transmitter and receiver:
|
||||
- carrier frequency
|
||||
- bit rate
|
||||
- frequency deviation
|
||||
- sync word
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration#cc1101
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// CC1101 has the following connections:
|
||||
// CS pin: 10
|
||||
// GDO0 pin: 2
|
||||
// RST pin: unused
|
||||
// GDO2 pin: 3 (optional)
|
||||
CC1101 radio = new Module(10, 2, RADIOLIB_NC, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//CC1101 radio = RadioShield.ModuleA;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize CC1101 with default settings
|
||||
Serial.print(F("[CC1101] Initializing ... "));
|
||||
int state = radio.begin();
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while (true);
|
||||
}
|
||||
|
||||
// set the function that will be called
|
||||
// when new packet is received
|
||||
radio.setPacketReceivedAction(setFlag);
|
||||
|
||||
// start listening for packets
|
||||
Serial.print(F("[CC1101] Starting to listen ... "));
|
||||
state = radio.startReceive();
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while (true);
|
||||
}
|
||||
|
||||
// if needed, 'listen' mode can be disabled by calling
|
||||
// any of the following methods:
|
||||
//
|
||||
// radio.standby()
|
||||
// radio.sleep()
|
||||
// radio.transmit();
|
||||
// radio.receive();
|
||||
// radio.readData();
|
||||
}
|
||||
|
||||
// flag to indicate that a packet was received
|
||||
volatile bool receivedFlag = false;
|
||||
|
||||
// this function is called when a complete packet
|
||||
// is received by the module
|
||||
// IMPORTANT: this function MUST be 'void' type
|
||||
// and MUST NOT have any arguments!
|
||||
#if defined(ESP8266) || defined(ESP32)
|
||||
ICACHE_RAM_ATTR
|
||||
#endif
|
||||
void setFlag(void) {
|
||||
// we got a packet, set the flag
|
||||
receivedFlag = true;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// check if the flag is set
|
||||
if(receivedFlag) {
|
||||
// reset flag
|
||||
receivedFlag = false;
|
||||
|
||||
// you can read received data as an Arduino String
|
||||
String str;
|
||||
int state = radio.readData(str);
|
||||
|
||||
// you can also read received data as byte array
|
||||
/*
|
||||
byte byteArr[8];
|
||||
int numBytes = radio.getPacketLength();
|
||||
int state = radio.readData(byteArr, numBytes);
|
||||
*/
|
||||
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
// packet was successfully received
|
||||
Serial.println(F("[CC1101] Received packet!"));
|
||||
|
||||
// print data of the packet
|
||||
Serial.print(F("[CC1101] Data:\t\t"));
|
||||
Serial.println(str);
|
||||
|
||||
// print RSSI (Received Signal Strength Indicator)
|
||||
// of the last received packet
|
||||
Serial.print(F("[CC1101] RSSI:\t\t"));
|
||||
Serial.print(radio.getRSSI());
|
||||
Serial.println(F(" dBm"));
|
||||
|
||||
// print LQI (Link Quality Indicator)
|
||||
// of the last received packet, lower is better
|
||||
Serial.print(F("[CC1101] LQI:\t\t"));
|
||||
Serial.println(radio.getLQI());
|
||||
|
||||
} else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
|
||||
// packet was received, but is malformed
|
||||
Serial.println(F("CRC error!"));
|
||||
|
||||
} else {
|
||||
// some other error occurred
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
|
||||
}
|
||||
|
||||
// put module back to listen mode
|
||||
radio.startReceive();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
/*
|
||||
RadioLib CC1101 Settings Example
|
||||
|
||||
This example shows how to change all the properties of RF69 radio.
|
||||
RadioLib currently supports the following settings:
|
||||
- pins (SPI slave select, digital IO 0, digital IO 1)
|
||||
- carrier frequency
|
||||
- bit rate
|
||||
- receiver bandwidth
|
||||
- allowed frequency deviation
|
||||
- output power during transmission
|
||||
- sync word
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration#cc1101
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// CC1101 has the following connections:
|
||||
// CS pin: 10
|
||||
// GDO0 pin: 2
|
||||
// RST pin: unused
|
||||
// GDO2 pin: 3 (optional)
|
||||
CC1101 radio1 = new Module(10, 2, RADIOLIB_NC, 3);
|
||||
|
||||
// second CC1101 has different connections:
|
||||
// CS pin: 9
|
||||
// GDO0 pin: 4
|
||||
// RST pin: unused
|
||||
// GDO2 pin: 5 (optional)
|
||||
CC1101 radio2 = new Module(9, 4, RADIOLIB_NC, 5);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//CC1101 radio3 = RadioShield.ModuleB;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize CC1101 with default settings
|
||||
Serial.print(F("[CC1101] Initializing ... "));
|
||||
int state = radio1.begin();
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while (true);
|
||||
}
|
||||
|
||||
// initialize CC1101 with non-default settings
|
||||
Serial.print(F("[CC1101] Initializing ... "));
|
||||
// carrier frequency: 434.0 MHz
|
||||
// bit rate: 32.0 kbps
|
||||
// frequency deviation: 60.0 kHz
|
||||
// Rx bandwidth: 250.0 kHz
|
||||
// output power: 7 dBm
|
||||
// preamble length: 32 bits
|
||||
state = radio2.begin(434.0, 32.0, 60.0, 250.0, 7, 32);
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while (true);
|
||||
}
|
||||
|
||||
// you can also change the settings at runtime
|
||||
// and check if the configuration was changed successfully
|
||||
|
||||
// set carrier frequency to 433.5 MHz
|
||||
if (radio1.setFrequency(433.5) == RADIOLIB_ERR_INVALID_FREQUENCY) {
|
||||
Serial.println(F("[CC1101] Selected frequency is invalid for this module!"));
|
||||
while (true);
|
||||
}
|
||||
|
||||
// set bit rate to 100.0 kbps
|
||||
state = radio1.setBitRate(100.0);
|
||||
if (state == RADIOLIB_ERR_INVALID_BIT_RATE) {
|
||||
Serial.println(F("[CC1101] Selected bit rate is invalid for this module!"));
|
||||
while (true);
|
||||
} else if (state == RADIOLIB_ERR_INVALID_BIT_RATE_BW_RATIO) {
|
||||
Serial.println(F("[CC1101] Selected bit rate to bandwidth ratio is invalid!"));
|
||||
Serial.println(F("[CC1101] Increase receiver bandwidth to set this bit rate."));
|
||||
while (true);
|
||||
}
|
||||
|
||||
// set receiver bandwidth to 250.0 kHz
|
||||
if (radio1.setRxBandwidth(250.0) == RADIOLIB_ERR_INVALID_RX_BANDWIDTH) {
|
||||
Serial.println(F("[CC1101] Selected receiver bandwidth is invalid for this module!"));
|
||||
while (true);
|
||||
}
|
||||
|
||||
// set allowed frequency deviation to 10.0 kHz
|
||||
if (radio1.setFrequencyDeviation(10.0) == RADIOLIB_ERR_INVALID_FREQUENCY_DEVIATION) {
|
||||
Serial.println(F("[CC1101] Selected frequency deviation is invalid for this module!"));
|
||||
while (true);
|
||||
}
|
||||
|
||||
// set output power to 5 dBm
|
||||
if (radio1.setOutputPower(5) == RADIOLIB_ERR_INVALID_OUTPUT_POWER) {
|
||||
Serial.println(F("[CC1101] Selected output power is invalid for this module!"));
|
||||
while (true);
|
||||
}
|
||||
|
||||
// 2 bytes can be set as sync word
|
||||
if (radio1.setSyncWord(0x01, 0x23) == RADIOLIB_ERR_INVALID_SYNC_WORD) {
|
||||
Serial.println(F("[CC1101] Selected sync word is invalid for this module!"));
|
||||
while (true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// nothing here
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
RadioLib CC1101 Transmit to Address Example
|
||||
|
||||
This example transmits packets using CC1101 FSK radio
|
||||
module. Packets can have 1-byte address of the
|
||||
destination node. After setting node address, this node
|
||||
will automatically filter out any packets that do not
|
||||
contain either node address or broadcast addresses.
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration#cc1101
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// CC1101 has the following connections:
|
||||
// CS pin: 10
|
||||
// GDO0 pin: 2
|
||||
// RST pin: unused
|
||||
// GDO2 pin: 3 (optional)
|
||||
CC1101 radio = new Module(10, 2, RADIOLIB_NC, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//CC1101 radio = RadioShield.ModuleA;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize CC1101 with default settings
|
||||
Serial.print(F("[CC1101] Initializing ... "));
|
||||
int state = radio.begin();
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while (true);
|
||||
}
|
||||
|
||||
// set node address
|
||||
// NOTE: Calling this method will automatically enable
|
||||
// address filtering. CC1101 also allows to set
|
||||
// number of broadcast address (0/1/2).
|
||||
// The following sets one broadcast address 0x00.
|
||||
// When setting two broadcast addresses, 0x00 and
|
||||
// 0xFF will be used.
|
||||
Serial.print(F("[CC1101] Setting node address ... "));
|
||||
state = radio.setNodeAddress(0x01, 1);
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while (true);
|
||||
}
|
||||
|
||||
// address filtering can also be disabled
|
||||
// NOTE: Calling this method will also erase previously
|
||||
// set node address
|
||||
/*
|
||||
Serial.print(F("[CC1101] Disabling address filtering ... "));
|
||||
state == radio.disableAddressFiltering();
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[CC1101] Transmitting packet ... "));
|
||||
|
||||
// you can transmit C-string or Arduino string up to 63 characters long
|
||||
int state = radio.transmit("Hello World!");
|
||||
|
||||
// you can also transmit byte array up to 63 bytes long
|
||||
/*
|
||||
byte byteArr[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};
|
||||
int state = radio.transmit(byteArr, 8);
|
||||
*/
|
||||
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
// the packet was successfully transmitted
|
||||
Serial.println(F("success!"));
|
||||
|
||||
} else if (state == RADIOLIB_ERR_PACKET_TOO_LONG) {
|
||||
// the supplied packet was longer than 255 bytes
|
||||
Serial.println(F("too long!"));
|
||||
|
||||
} else {
|
||||
// some other error occurred
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
|
||||
}
|
||||
|
||||
// wait for a second before transmitting again
|
||||
delay(1000);
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
RadioLib CC1101 Blocking Transmit Example
|
||||
|
||||
This example transmits packets using CC1101 FSK radio module.
|
||||
Each packet contains up to 64 bytes of data, in the form of:
|
||||
- Arduino String
|
||||
- null-terminated char array (C-string)
|
||||
- arbitrary binary data (byte array)
|
||||
|
||||
Using blocking transmit is not recommended, as it will lead
|
||||
to inefficient use of processor time!
|
||||
Instead, interrupt transmit is recommended.
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration#cc1101
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// CC1101 has the following connections:
|
||||
// CS pin: 10
|
||||
// GDO0 pin: 2
|
||||
// RST pin: unused
|
||||
// GDO2 pin: 3
|
||||
CC1101 radio = new Module(10, 2, RADIOLIB_NC, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//CC1101 radio = RadioShield.ModuleA;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize CC1101 with default settings
|
||||
Serial.print(F("[CC1101] Initializing ... "));
|
||||
int state = radio.begin();
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while (true);
|
||||
}
|
||||
}
|
||||
|
||||
// counter to keep track of transmitted packets
|
||||
int count = 0;
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[CC1101] Transmitting packet ... "));
|
||||
|
||||
// you can transmit C-string or Arduino string up to 63 characters long
|
||||
String str = "Hello World! #" + String(count++);
|
||||
int state = radio.transmit(str);
|
||||
|
||||
// you can also transmit byte array up to 63 bytes long
|
||||
/*
|
||||
byte byteArr[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};
|
||||
int state = radio.transmit(byteArr, 8);
|
||||
*/
|
||||
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
// the packet was successfully transmitted
|
||||
Serial.println(F("success!"));
|
||||
|
||||
} else if (state == RADIOLIB_ERR_PACKET_TOO_LONG) {
|
||||
// the supplied packet was longer than 64 bytes
|
||||
Serial.println(F("too long!"));
|
||||
|
||||
} else {
|
||||
// some other error occurred
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
|
||||
}
|
||||
|
||||
// wait for a second before transmitting again
|
||||
delay(1000);
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
/*
|
||||
RadioLib CC1101 Transmit with Interrupts Example
|
||||
|
||||
This example transmits packets using CC1101 FSK radio module.
|
||||
Once a packet is transmitted, an interrupt is triggered.
|
||||
Each packet contains up to 64 bytes of data, in the form of:
|
||||
- Arduino String
|
||||
- null-terminated char array (C-string)
|
||||
- arbitrary binary data (byte array)
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration#cc1101
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// CC1101 has the following connections:
|
||||
// CS pin: 10
|
||||
// GDO0 pin: 2
|
||||
// RST pin: unused
|
||||
// GDO2 pin: 3
|
||||
CC1101 radio = new Module(10, 2, RADIOLIB_NC, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//CC1101 radio = RadioShield.ModuleA;
|
||||
|
||||
// save transmission state between loops
|
||||
int transmissionState = RADIOLIB_ERR_NONE;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize CC1101 with default settings
|
||||
Serial.print(F("[CC1101] Initializing ... "));
|
||||
int state = radio.begin();
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while (true);
|
||||
}
|
||||
|
||||
// set the function that will be called
|
||||
// when packet transmission is finished
|
||||
radio.setPacketSentAction(setFlag);
|
||||
|
||||
// start transmitting the first packet
|
||||
Serial.print(F("[CC1101] Sending first packet ... "));
|
||||
|
||||
// you can transmit C-string or Arduino string up to
|
||||
// 64 characters long
|
||||
transmissionState = radio.startTransmit("Hello World!");
|
||||
|
||||
// you can also transmit byte array up to 64 bytes long
|
||||
/*
|
||||
byte byteArr[] = {0x01, 0x23, 0x45, 0x56,
|
||||
0x78, 0xAB, 0xCD, 0xEF};
|
||||
state = radio.startTransmit(byteArr, 8);
|
||||
*/
|
||||
}
|
||||
|
||||
// flag to indicate that a packet was sent
|
||||
volatile bool transmittedFlag = false;
|
||||
|
||||
// this function is called when a complete packet
|
||||
// is transmitted by the module
|
||||
// IMPORTANT: this function MUST be 'void' type
|
||||
// and MUST NOT have any arguments!
|
||||
#if defined(ESP8266) || defined(ESP32)
|
||||
ICACHE_RAM_ATTR
|
||||
#endif
|
||||
void setFlag(void) {
|
||||
// we sent a packet, set the flag
|
||||
transmittedFlag = true;
|
||||
}
|
||||
|
||||
// counter to keep track of transmitted packets
|
||||
int count = 0;
|
||||
|
||||
void loop() {
|
||||
// check if the previous transmission finished
|
||||
if(transmittedFlag) {
|
||||
// reset flag
|
||||
transmittedFlag = false;
|
||||
|
||||
if (transmissionState == RADIOLIB_ERR_NONE) {
|
||||
// packet was successfully sent
|
||||
Serial.println(F("transmission finished!"));
|
||||
|
||||
// NOTE: when using interrupt-driven transmit method,
|
||||
// it is not possible to automatically measure
|
||||
// transmission data rate using getDataRate()
|
||||
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(transmissionState);
|
||||
|
||||
}
|
||||
|
||||
// clean up after transmission is finished
|
||||
// this will ensure transmitter is disabled,
|
||||
// RF switch is powered down etc.
|
||||
radio.finishTransmit();
|
||||
|
||||
// wait a second before transmitting again
|
||||
delay(1000);
|
||||
|
||||
// send another one
|
||||
Serial.print(F("[CC1101] Sending another packet ... "));
|
||||
|
||||
// you can transmit C-string or Arduino string up to
|
||||
// 256 characters long
|
||||
String str = "Hello World! #" + String(count++);
|
||||
transmissionState = radio.startTransmit(str);
|
||||
|
||||
// you can also transmit byte array up to 256 bytes long
|
||||
/*
|
||||
byte byteArr[] = {0x01, 0x23, 0x45, 0x67,
|
||||
0x89, 0xAB, 0xCD, 0xEF};
|
||||
int state = radio.startTransmit(byteArr, 8);
|
||||
*/
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
RadioLib FSK4 Transmit Example
|
||||
|
||||
This example sends an example FSK-4 'Horus Binary' message
|
||||
using SX1278's FSK modem.
|
||||
|
||||
This signal can be demodulated using a SSB demodulator (SDR or otherwise),
|
||||
and horusdemodlib: https://github.com/projecthorus/horusdemodlib/wiki
|
||||
|
||||
Other modules that can be used for FSK4:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- SX126x
|
||||
- nRF24
|
||||
- Si443x/RFM2x
|
||||
- SX128x
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create FSK4 client instance using the FSK module
|
||||
FSK4Client fsk4(&radio);
|
||||
|
||||
// An encoded Horus Binary telemetry packet.
|
||||
// Refer here for packet format information:
|
||||
// https://github.com/projecthorus/horusdemodlib/wiki/2---Modem-Details#horus-binary-v1-mode-4-fsk
|
||||
// After demodulation, deinterleaving, and descrambling, this results in a packet:
|
||||
// 00000001172D0000000000000000D20463010AFF2780
|
||||
// This decodes to the Habitat-compatible telemetry string:
|
||||
// $$4FSKTEST,0,01:23:45,0.00000,0.00000,1234,99,1,10,5.00*ABCD
|
||||
int horusPacketLen = 45;
|
||||
byte horusPacket[] = {
|
||||
0x45, 0x24, 0x24, 0x48, 0x2F, 0x12, 0x16, 0x08, 0x15, 0xC1,
|
||||
0x49, 0xB2, 0x06, 0xFC, 0x92, 0xEB, 0x93, 0xD7, 0xEE, 0x5D,
|
||||
0x35, 0xA0, 0x91, 0xDA, 0x8D, 0x5F, 0x85, 0x6B, 0x63, 0x03,
|
||||
0x6B, 0x60, 0xEA, 0xFE, 0x55, 0x9D, 0xF1, 0xAB, 0xE5, 0x5E,
|
||||
0xDB, 0x7C, 0xDB, 0x21, 0x5A, 0x19
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules for FSK4
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize FSK4 client
|
||||
// NOTE: FSK4 frequency shift will be rounded
|
||||
// to the nearest multiple of frequency step size.
|
||||
// The exact value depends on the module:
|
||||
// SX127x/RFM9x - 61 Hz
|
||||
// RF69 - 61 Hz
|
||||
// CC1101 - 397 Hz
|
||||
// SX126x - 1 Hz
|
||||
// nRF24 - 1000000 Hz
|
||||
// Si443x/RFM2x - 156 Hz
|
||||
// SX128x - 198 Hz
|
||||
Serial.print(F("[FSK4] Initializing ... "));
|
||||
// low ("space") frequency: 434.0 MHz
|
||||
// frequency shift: 270 Hz
|
||||
// baud rate: 100 baud
|
||||
state = fsk4.begin(434.0, 270, 100);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// sometimes, it may be needed to set some manual corrections
|
||||
// this can be done for tone frequencies,
|
||||
// as well as tone lengths
|
||||
/*
|
||||
// set frequency shift offsets to -120, 60, 0 and 60 Hz and decrease tone length to 95%
|
||||
int offsets[4] = { -120, -60, 0, 60 };
|
||||
Serial.print(F("[FSK4] Setting corrections ... "));
|
||||
state = fsk4.setCorrection(offsets, 0.95);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[FSK4] Sending FSK4 data packet ... "));
|
||||
|
||||
// send out idle condition for 1000 ms
|
||||
fsk4.idle();
|
||||
delay(1000);
|
||||
|
||||
// FSK4Client supports binary write methods
|
||||
|
||||
// send some bytes as a preamble
|
||||
for(int i = 0; i < 8; i++) {
|
||||
fsk4.write(0x1B);
|
||||
}
|
||||
|
||||
// now send the encoded packet
|
||||
fsk4.write(horusPacket, horusPacketLen);
|
||||
|
||||
Serial.println(F("done!"));
|
||||
|
||||
// wait for a second before transmitting again
|
||||
delay(1000);
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
RadioLib FSK4 Transmit AFSK Example
|
||||
|
||||
This example sends an example FSK-4 'Horus Binary' message
|
||||
using SX1278's FSK modem. The data is modulated as AFSK.
|
||||
|
||||
This signal can be demodulated using an FM demodulator (SDR or otherwise),
|
||||
and horusdemodlib: https://github.com/projecthorus/horusdemodlib/wiki
|
||||
|
||||
Other modules that can be used for FSK4:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- Si443x/RFM2x
|
||||
- SX126x/LLCC68 (only devices without TCXO!)
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create AFSK client instance using the FSK module
|
||||
// this requires connection to the module direct
|
||||
// input pin, here connected to Arduino pin 5
|
||||
// SX127x/RFM9x: DIO2
|
||||
// RF69: DIO2
|
||||
// SX1231: DIO2
|
||||
// CC1101: GDO2
|
||||
// Si443x/RFM2x: GPIO
|
||||
// SX126x/LLCC68: DIO2
|
||||
AFSKClient audio(&radio, 5);
|
||||
|
||||
// create FSK4 client instance using the AFSK instance
|
||||
FSK4Client fsk4(&audio);
|
||||
|
||||
// An encoded Horus Binary telemetry packet.
|
||||
// Refer here for packet format information:
|
||||
// https://github.com/projecthorus/horusdemodlib/wiki/2---Modem-Details#horus-binary-v1-mode-4-fsk
|
||||
// After demodulation, deinterleaving, and descrambling, this results in a packet:
|
||||
// 00000001172D0000000000000000D20463010AFF2780
|
||||
// This decodes to the Habitat-compatible telemetry string:
|
||||
// $$4FSKTEST,0,01:23:45,0.00000,0.00000,1234,99,1,10,5.00*ABCD
|
||||
int horusPacketLen = 45;
|
||||
byte horusPacket[] = {
|
||||
0x45, 0x24, 0x24, 0x48, 0x2F, 0x12, 0x16, 0x08, 0x15, 0xC1,
|
||||
0x49, 0xB2, 0x06, 0xFC, 0x92, 0xEB, 0x93, 0xD7, 0xEE, 0x5D,
|
||||
0x35, 0xA0, 0x91, 0xDA, 0x8D, 0x5F, 0x85, 0x6B, 0x63, 0x03,
|
||||
0x6B, 0x60, 0xEA, 0xFE, 0x55, 0x9D, 0xF1, 0xAB, 0xE5, 0x5E,
|
||||
0xDB, 0x7C, 0xDB, 0x21, 0x5A, 0x19
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules for RTTY
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize FSK4 client
|
||||
// NOTE: Unlike FSK FSK4, AFSK requires no rounding of
|
||||
// the frequency shift.
|
||||
Serial.print(F("[FSK4] Initializing ... "));
|
||||
// lowest ("space") frequency: 400 Hz
|
||||
// frequency shift: 270 Hz
|
||||
// baud rate: 100 baud
|
||||
state = fsk4.begin(400, 270, 100);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// sometimes, it may be needed to set some manual corrections
|
||||
// this can be done for tone frequencies,
|
||||
// as well as tone lengths
|
||||
/*
|
||||
// set audio tone offsets to -10, 20, 0 and 5 Hz and decrease tone length to 95%
|
||||
int offsets[4] = { -10, 20, 0, 5 };
|
||||
Serial.print(F("[FSK4] Setting corrections ... "));
|
||||
state = fsk4.setCorrection(offsets, 0.95);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[FSK4] Sending FSK4 data packet ... "));
|
||||
|
||||
// send out idle condition for 500 ms
|
||||
fsk4.idle();
|
||||
delay(1000);
|
||||
|
||||
// FSK4Client supports binary write methods
|
||||
|
||||
// send some bytes as a preamble
|
||||
for(int i = 0; i < 8; i++) {
|
||||
fsk4.write(0x1B);
|
||||
}
|
||||
|
||||
// now send the encoded packet
|
||||
fsk4.write(horusPacket, horusPacketLen);
|
||||
|
||||
Serial.println(F("done!"));
|
||||
|
||||
// wait for a second before transmitting again
|
||||
delay(1000);
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
RadioLib Hellschreiber Transmit Example
|
||||
|
||||
This example sends Hellschreiber message using
|
||||
SX1278's FSK modem.
|
||||
|
||||
Other modules that can be used for Hellschreiber:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- SX126x
|
||||
- nRF24
|
||||
- Si443x/RFM2x
|
||||
- SX128x
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create Hellschreiber client instance using the FSK module
|
||||
HellClient hell(&radio);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules for Morse code
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize Hellschreiber client
|
||||
Serial.print(F("[Hell] Initializing ... "));
|
||||
// base frequency: 434.0 MHz
|
||||
// speed: 122.5 Baud ("Feld Hell")
|
||||
state = hell.begin(434.0);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[Hell] Sending Hellschreiber data ... "));
|
||||
|
||||
// HellClient supports all methods of the Serial class
|
||||
// NOTE: Lower case letter will be capitalized.
|
||||
|
||||
// Arduino String class
|
||||
String aStr = "Arduino String";
|
||||
hell.print(aStr);
|
||||
|
||||
// character array (C-String)
|
||||
hell.print("C-String");
|
||||
|
||||
// string saved in flash
|
||||
hell.print(F("Flash String"));
|
||||
|
||||
// character
|
||||
hell.print('c');
|
||||
|
||||
// byte
|
||||
// formatting DEC/HEX/OCT/BIN is supported for
|
||||
// any integer type (byte/int/long)
|
||||
hell.print(255, HEX);
|
||||
|
||||
// integer number
|
||||
int i = 1000;
|
||||
hell.print(i);
|
||||
|
||||
// floating point number
|
||||
// NOTE: println() has no effect on the transmission,
|
||||
// and is only kept for compatibility reasons.
|
||||
float f = -3.1415;
|
||||
hell.println(f, 3);
|
||||
|
||||
// custom glyph - must be a 7 byte array of rows 7 pixels long
|
||||
uint8_t customGlyph[] = { 0b0000000, 0b0010100, 0b0010100, 0b0000000, 0b0100010, 0b0011100, 0b0000000 };
|
||||
hell.printGlyph(customGlyph);
|
||||
|
||||
Serial.println(F("done!"));
|
||||
|
||||
// wait for a second before transmitting again
|
||||
delay(1000);
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/*
|
||||
RadioLib Hellschreiber Transmit AFSK Example
|
||||
|
||||
This example sends Hellschreiber message using
|
||||
SX1278's FSK modem. The data is modulated
|
||||
as AFSK.
|
||||
|
||||
Other modules that can be used for Hellschreiber
|
||||
with AFSK modulation:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- Si443x/RFM2x
|
||||
- SX126x/LLCC68 (only devices without TCXO!)
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create AFSK client instance using the FSK module
|
||||
// this requires connection to the module direct
|
||||
// input pin, here connected to Arduino pin 5
|
||||
// SX127x/RFM9x: DIO2
|
||||
// RF69: DIO2
|
||||
// SX1231: DIO2
|
||||
// CC1101: GDO2
|
||||
// Si443x/RFM2x: GPIO
|
||||
// SX126x/LLCC68: DIO2
|
||||
AFSKClient audio(&radio, 5);
|
||||
|
||||
// create Hellschreiber client instance using the AFSK instance
|
||||
HellClient hell(&audio);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules for Morse code
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize Hellschreiber client
|
||||
Serial.print(F("[Hell] Initializing ... "));
|
||||
// AFSK tone frequency: 400 Hz
|
||||
// speed: 122.5 Baud ("Feld Hell")
|
||||
state = hell.begin(400);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[Hell] Sending Hellschreiber data ... "));
|
||||
|
||||
// HellClient supports all methods of the Serial class
|
||||
// NOTE: Lower case letter will be capitalized.
|
||||
|
||||
// Arduino String class
|
||||
String aStr = "Arduino String";
|
||||
hell.print(aStr);
|
||||
|
||||
// character array (C-String)
|
||||
hell.print("C-String");
|
||||
|
||||
// string saved in flash
|
||||
hell.print(F("Flash String"));
|
||||
|
||||
// in AFSK mode, it is possible to invert the text colors
|
||||
// use white text on black background
|
||||
hell.setInversion(true);
|
||||
hell.print("Inverted String");
|
||||
hell.setInversion(false);
|
||||
|
||||
// character
|
||||
hell.print('c');
|
||||
|
||||
// byte
|
||||
// formatting DEC/HEX/OCT/BIN is supported for
|
||||
// any integer type (byte/int/long)
|
||||
hell.print(255, HEX);
|
||||
|
||||
// integer number
|
||||
int i = 1000;
|
||||
hell.print(i);
|
||||
|
||||
// floating point number
|
||||
// NOTE: println() has no effect on the transmission,
|
||||
// and is only kept for compatibility reasons.
|
||||
float f = -3.1415;
|
||||
hell.println(f, 3);
|
||||
|
||||
// custom glyph - must be a 7 byte array of rows 7 pixels long
|
||||
uint8_t customGlyph[] = { 0b0000000, 0b0010100, 0b0010100, 0b0000000, 0b0100010, 0b0011100, 0b0000000 };
|
||||
hell.printGlyph(customGlyph);
|
||||
|
||||
Serial.println(F("done!"));
|
||||
|
||||
// wait for a second before transmitting again
|
||||
delay(1000);
|
||||
}
|
@ -0,0 +1,171 @@
|
||||
/*
|
||||
RadioLib LoRaWAN End Device Example
|
||||
|
||||
This example joins a LoRaWAN network and will send
|
||||
uplink packets. Before you start, you will have to
|
||||
register your device at https://www.thethingsnetwork.org/
|
||||
After your device is registered, you can run this example.
|
||||
The device will join the network and start uploading data.
|
||||
|
||||
NOTE: LoRaWAN v1.1 requires storing parameters persistently!
|
||||
RadioLib does this by using EEPROM (persistent storage),
|
||||
by default starting at address 0 and using 448 bytes.
|
||||
If you already use EEPROM in your application,
|
||||
you will have to either avoid this range, or change it
|
||||
by setting a different start address by changing the value of
|
||||
RADIOLIB_HAL_PERSISTENT_STORAGE_BASE macro, either
|
||||
during build or in src/BuildOpt.h.
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1262 has the following pin order:
|
||||
// Module(NSS/CS, DIO1, RESET, BUSY)
|
||||
// SX1262 radio = new Module(8, 14, 12, 13);
|
||||
|
||||
// SX1278 has the following pin order:
|
||||
// Module(NSS/CS, DIO0, RESET, DIO1)
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// create the node instance on the EU-868 band
|
||||
// using the radio module and the encryption key
|
||||
// make sure you are using the correct band
|
||||
// based on your geographical location!
|
||||
LoRaWANNode node(&radio, &EU868);
|
||||
|
||||
// for fixed bands with subband selection
|
||||
// such as US915 and AU915, you must specify
|
||||
// the subband that matches the Frequency Plan
|
||||
// that you selected on your LoRaWAN console
|
||||
/*
|
||||
LoRaWANNode node(&radio, &US915, 2);
|
||||
*/
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize radio (SX1262 / SX1278 / ... ) with default settings
|
||||
Serial.print(F("[Radio] Initializing ... "));
|
||||
int state = radio.begin();
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// application identifier - pre-LoRaWAN 1.1.0, this was called appEUI
|
||||
// when adding new end device in TTN, you will have to enter this number
|
||||
// you can pick any number you want, but it has to be unique
|
||||
uint64_t joinEUI = 0x12AD1011B0C0FFEE;
|
||||
|
||||
// device identifier - this number can be anything
|
||||
// when adding new end device in TTN, you can generate this number,
|
||||
// or you can set any value you want, provided it is also unique
|
||||
uint64_t devEUI = 0x70B3D57ED005E120;
|
||||
|
||||
// select some encryption keys which will be used to secure the communication
|
||||
// there are two of them - network key and application key
|
||||
// because LoRaWAN uses AES-128, the key MUST be 16 bytes (or characters) long
|
||||
|
||||
// network key is the ASCII string "topSecretKey1234"
|
||||
uint8_t nwkKey[] = { 0x74, 0x6F, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65,
|
||||
0x74, 0x4B, 0x65, 0x79, 0x31, 0x32, 0x33, 0x34 };
|
||||
|
||||
// application key is the ASCII string "aDifferentKeyABC"
|
||||
uint8_t appKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65,
|
||||
0x6E, 0x74, 0x4B, 0x65, 0x79, 0x41, 0x42, 0x43 };
|
||||
|
||||
// prior to LoRaWAN 1.1.0, only a single "nwkKey" is used
|
||||
// when connecting to LoRaWAN 1.0 network, "appKey" will be disregarded
|
||||
// and can be set to NULL
|
||||
|
||||
|
||||
// on EEPROM-enabled boards, after the device has been activated,
|
||||
// the session can be restored without rejoining after device power cycle
|
||||
// this is intrinsically done when calling `beginOTAA()` with the same keys
|
||||
// in that case, the function will not need to transmit a JoinRequest
|
||||
|
||||
// now we can start the activation
|
||||
// this can take up to 10 seconds, and requires a LoRaWAN gateway in range
|
||||
// a specific starting-datarate can be selected in dynamic bands (e.g. EU868):
|
||||
/*
|
||||
uint8_t joinDr = 4;
|
||||
state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, joinDr);
|
||||
*/
|
||||
Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... "));
|
||||
state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey);
|
||||
|
||||
if(state >= RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
delay(2000); // small delay between joining and uplink
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// counter to keep track of transmitted packets
|
||||
int count = 0;
|
||||
|
||||
void loop() {
|
||||
// send uplink to port 10
|
||||
Serial.print(F("[LoRaWAN] Sending uplink packet ... "));
|
||||
String strUp = "Hello!" + String(count++);
|
||||
String strDown;
|
||||
int state = node.sendReceive(strUp, 10, strDown);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("received a downlink!"));
|
||||
|
||||
// print data of the packet (if there are any)
|
||||
Serial.print(F("[LoRaWAN] Data:\t\t"));
|
||||
if(strDown.length() > 0) {
|
||||
Serial.println(strDown);
|
||||
} else {
|
||||
Serial.println(F("<MAC commands only>"));
|
||||
}
|
||||
|
||||
// print RSSI (Received Signal Strength Indicator)
|
||||
Serial.print(F("[LoRaWAN] RSSI:\t\t"));
|
||||
Serial.print(radio.getRSSI());
|
||||
Serial.println(F(" dBm"));
|
||||
|
||||
// print SNR (Signal-to-Noise Ratio)
|
||||
Serial.print(F("[LoRaWAN] SNR:\t\t"));
|
||||
Serial.print(radio.getSNR());
|
||||
Serial.println(F(" dB"));
|
||||
|
||||
// print frequency error
|
||||
Serial.print(F("[LoRaWAN] Frequency error:\t"));
|
||||
Serial.print(radio.getFrequencyError());
|
||||
Serial.println(F(" Hz"));
|
||||
|
||||
} else if(state == RADIOLIB_ERR_RX_TIMEOUT) {
|
||||
Serial.println(F("no downlink!"));
|
||||
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
// on EEPROM enabled boards, you can save the current session
|
||||
// by calling "saveSession" which allows retrieving the session after reboot or deepsleep
|
||||
node.saveSession();
|
||||
|
||||
// wait before sending another packet
|
||||
uint32_t minimumDelay = 60000; // try to send once every minute
|
||||
uint32_t interval = node.timeUntilUplink(); // calculate minimum duty cycle delay (per law!)
|
||||
uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows
|
||||
|
||||
delay(delayMs);
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
/*
|
||||
RadioLib LoRaWAN End Device ABP Example
|
||||
|
||||
This example sets up a LoRaWAN node using ABP (activation
|
||||
by personalization). Before you start, you will have to
|
||||
register your device at https://www.thethingsnetwork.org/
|
||||
After your device is registered, you can run this example.
|
||||
The device will start uploading data directly,
|
||||
without having to join the network.
|
||||
|
||||
NOTE: LoRaWAN v1.1 requires storing parameters persistently!
|
||||
RadioLib does this by using EEPROM (persistent storage),
|
||||
by default starting at address 0 and using 448 bytes.
|
||||
If you already use EEPROM in your application,
|
||||
you will have to either avoid this range, or change it
|
||||
by setting a different start address by changing the value of
|
||||
RADIOLIB_HAL_PERSISTENT_STORAGE_BASE macro, either
|
||||
during build or in src/BuildOpt.h.
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1262 has the following pin order:
|
||||
// Module(NSS/CS, DIO1, RESET, BUSY)
|
||||
// SX1262 radio = new Module(8, 14, 12, 13);
|
||||
|
||||
// SX1278 has the following pin order:
|
||||
// Module(NSS/CS, DIO0, RESET, DIO1)
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// create the node instance on the EU-868 band
|
||||
// using the radio module and the encryption key
|
||||
// make sure you are using the correct band
|
||||
// based on your geographical location!
|
||||
LoRaWANNode node(&radio, &EU868);
|
||||
|
||||
// for fixed bands with subband selection
|
||||
// such as US915 and AU915, you must specify
|
||||
// the subband that matches the Frequency Plan
|
||||
// that you selected on your LoRaWAN console
|
||||
/*
|
||||
LoRaWANNode node(&radio, &US915, 2);
|
||||
*/
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize radio (SX1262 / SX1278 / ... ) with default settings
|
||||
Serial.print(F("[Radio] Initializing ... "));
|
||||
int state = radio.begin();
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// device address - this number can be anything
|
||||
// when adding new end device in TTN, you can generate this number,
|
||||
// or you can set any value you want, provided it is unique
|
||||
uint32_t devAddr = 0x12345678;
|
||||
|
||||
// select some encryption keys which will be used to secure the communication
|
||||
// there are two of them - network key and application key
|
||||
// because LoRaWAN uses AES-128, the key MUST be 16 bytes (or characters) long
|
||||
|
||||
// network key is the ASCII string "topSecretKey1234"
|
||||
uint8_t nwkSKey[] = { 0x74, 0x6F, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65,
|
||||
0x74, 0x4B, 0x65, 0x79, 0x31, 0x32, 0x33, 0x34 };
|
||||
|
||||
// application key is the ASCII string "aDifferentKeyABC"
|
||||
uint8_t appSKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65,
|
||||
0x6E, 0x74, 0x4B, 0x65, 0x79, 0x41, 0x42, 0x43 };
|
||||
|
||||
// network key 2 is the ASCII string "topSecretKey5678"
|
||||
uint8_t fNwkSIntKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65,
|
||||
0x6E, 0x74, 0x4B, 0x65, 0x35, 0x36, 0x37, 0x38 };
|
||||
|
||||
// network key 3 is the ASCII string "aDifferentKeyDEF"
|
||||
uint8_t sNwkSIntKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65,
|
||||
0x6E, 0x74, 0x4B, 0x65, 0x79, 0x44, 0x45, 0x46 };
|
||||
|
||||
// prior to LoRaWAN 1.1.0, only a single "nwkKey" is used
|
||||
// when connecting to LoRaWAN 1.0 network, "appKey" will be disregarded
|
||||
// and can be set to NULL
|
||||
|
||||
|
||||
// if using EU868 on ABP in TTN, you need to set the SF for RX2 window manually
|
||||
/*
|
||||
node.rx2.drMax = 3;
|
||||
*/
|
||||
|
||||
// on EEPROM-enabled boards, after the device has been activated,
|
||||
// the session can be restored without rejoining after device power cycle
|
||||
// this is intrinsically done when calling `beginABP()` with the same keys
|
||||
// in that case, the function will not need to transmit a JoinRequest
|
||||
|
||||
// to start a LoRaWAN v1.0 session,
|
||||
// the user can remove the fNwkSIntKey and sNwkSIntKey
|
||||
/*
|
||||
state = node.beginABP(devAddr, nwkSKey, appSKey);
|
||||
*/
|
||||
|
||||
// start the device by directly providing the encryption keys and device address
|
||||
Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... "));
|
||||
state = node.beginABP(devAddr, nwkSKey, appSKey, fNwkSIntKey, sNwkSIntKey);
|
||||
if(state >= RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// counter to keep track of transmitted packets
|
||||
int count = 0;
|
||||
|
||||
void loop() {
|
||||
// send uplink to port 10
|
||||
Serial.print(F("[LoRaWAN] Sending uplink packet ... "));
|
||||
String strUp = "Hello!" + String(count++);
|
||||
String strDown;
|
||||
int state = node.sendReceive(strUp, 10, strDown);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("received a downlink!"));
|
||||
|
||||
// print data of the packet (if there are any)
|
||||
Serial.print(F("[LoRaWAN] Data:\t\t"));
|
||||
if(strDown.length() > 0) {
|
||||
Serial.println(strDown);
|
||||
} else {
|
||||
Serial.println(F("<MAC commands only>"));
|
||||
}
|
||||
|
||||
// print RSSI (Received Signal Strength Indicator)
|
||||
Serial.print(F("[LoRaWAN] RSSI:\t\t"));
|
||||
Serial.print(radio.getRSSI());
|
||||
Serial.println(F(" dBm"));
|
||||
|
||||
// print SNR (Signal-to-Noise Ratio)
|
||||
Serial.print(F("[LoRaWAN] SNR:\t\t"));
|
||||
Serial.print(radio.getSNR());
|
||||
Serial.println(F(" dB"));
|
||||
|
||||
// print frequency error
|
||||
Serial.print(F("[LoRaWAN] Frequency error:\t"));
|
||||
Serial.print(radio.getFrequencyError());
|
||||
Serial.println(F(" Hz"));
|
||||
|
||||
} else if(state == RADIOLIB_ERR_RX_TIMEOUT) {
|
||||
Serial.println(F("no downlink!"));
|
||||
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
// on EEPROM enabled boards, you can save the current session
|
||||
// by calling "saveSession" which allows retrieving the session after reboot or deepsleep
|
||||
node.saveSession();
|
||||
|
||||
// wait before sending another packet
|
||||
uint32_t minimumDelay = 60000; // try to send once every minute
|
||||
uint32_t interval = node.timeUntilUplink(); // calculate minimum duty cycle delay (per law!)
|
||||
uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows
|
||||
|
||||
delay(delayMs);
|
||||
}
|
@ -0,0 +1,298 @@
|
||||
/*
|
||||
RadioLib LoRaWAN End Device Reference Example
|
||||
|
||||
This example joins a LoRaWAN network and will send
|
||||
uplink packets. Before you start, you will have to
|
||||
register your device at https://www.thethingsnetwork.org/
|
||||
After your device is registered, you can run this example.
|
||||
The device will join the network and start uploading data.
|
||||
|
||||
Also, most of the possible and available functions are
|
||||
shown here for reference.
|
||||
|
||||
LoRaWAN v1.1 requires the use of EEPROM (persistent storage).
|
||||
Please refer to the 'persistent' example once you are familiar
|
||||
with LoRaWAN.
|
||||
Running this examples REQUIRES you to check "Resets DevNonces"
|
||||
on your LoRaWAN dashboard. Refer to the network's
|
||||
documentation on how to do this.
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1262 has the following pin order:
|
||||
// Module(NSS/CS, DIO1, RESET, BUSY)
|
||||
// SX1262 radio = new Module(8, 14, 12, 13);
|
||||
|
||||
// SX1278 has the following pin order:
|
||||
// Module(NSS/CS, DIO0, RESET, DIO1)
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// create the node instance on the EU-868 band
|
||||
// using the radio module and the encryption key
|
||||
// make sure you are using the correct band
|
||||
// based on your geographical location!
|
||||
LoRaWANNode node(&radio, &EU868);
|
||||
|
||||
// for fixed bands with subband selection
|
||||
// such as US915 and AU915, you must specify
|
||||
// the subband that matches the Frequency Plan
|
||||
// that you selected on your LoRaWAN console
|
||||
/*
|
||||
LoRaWANNode node(&radio, &US915, 2);
|
||||
*/
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize radio (SX1262 / SX1278 / ... ) with default settings
|
||||
Serial.print(F("[Radio] Initializing ... "));
|
||||
int state = radio.begin();
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// application identifier - pre-LoRaWAN 1.1.0, this was called appEUI
|
||||
// when adding new end device in TTN, you will have to enter this number
|
||||
// you can pick any number you want, but it has to be unique
|
||||
uint64_t joinEUI = 0x12AD1011B0C0FFEE;
|
||||
|
||||
// device identifier - this number can be anything
|
||||
// when adding new end device in TTN, you can generate this number,
|
||||
// or you can set any value you want, provided it is also unique
|
||||
uint64_t devEUI = 0x70B3D57ED005E120;
|
||||
|
||||
// select some encryption keys which will be used to secure the communication
|
||||
// there are two of them - network key and application key
|
||||
// because LoRaWAN uses AES-128, the key MUST be 16 bytes (or characters) long
|
||||
|
||||
// network key is the ASCII string "topSecretKey1234"
|
||||
uint8_t nwkKey[] = { 0x74, 0x6F, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65,
|
||||
0x74, 0x4B, 0x65, 0x79, 0x31, 0x32, 0x33, 0x34 };
|
||||
|
||||
// application key is the ASCII string "aDifferentKeyABC"
|
||||
uint8_t appKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65,
|
||||
0x6E, 0x74, 0x4B, 0x65, 0x79, 0x41, 0x42, 0x43 };
|
||||
|
||||
// prior to LoRaWAN 1.1.0, only a single "nwkKey" is used
|
||||
// when connecting to LoRaWAN 1.0 network, "appKey" will be disregarded
|
||||
// and can be set to NULL
|
||||
|
||||
|
||||
// now we can start the activation
|
||||
// this can take up to 10 seconds, and requires a LoRaWAN gateway in range
|
||||
// a specific starting-datarate can be selected in dynamic bands (e.g. EU868):
|
||||
/*
|
||||
uint8_t joinDr = 4;
|
||||
state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, joinDr);
|
||||
*/
|
||||
Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... "));
|
||||
state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey);
|
||||
|
||||
if(state >= RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
delay(2000); // small delay between joining and uplink
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
Serial.print("[LoRaWAN] DevAddr: ");
|
||||
Serial.println(node.getDevAddr(), HEX);
|
||||
|
||||
// on EEPROM-enabled boards, after the device has been activated,
|
||||
// the session can be restored without rejoining after device power cycle
|
||||
// this is intrinsically done when calling `beginOTAA()` with the same keys
|
||||
// or if you 'lost' the keys or don't want them included in your sketch
|
||||
// you can call `restore()`
|
||||
/*
|
||||
Serial.print(F("[LoRaWAN] Resuming previous session ... "));
|
||||
state = node.restore();
|
||||
if(state >= RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
*/
|
||||
|
||||
// disable the ADR algorithm
|
||||
node.setADR(false);
|
||||
|
||||
// set a fixed datarate
|
||||
node.setDatarate(5);
|
||||
// in order to set the datarate persistent across reboot/deepsleep, use the following:
|
||||
/*
|
||||
node.setDatarate(5, true);
|
||||
*/
|
||||
|
||||
// enable CSMA
|
||||
// this tries to minimize packet loss by searching for a free channel
|
||||
// before actually sending an uplink
|
||||
node.setCSMA(6, 2, true);
|
||||
|
||||
// enable or disable the dutycycle
|
||||
// the second argument specific allowed airtime per hour in milliseconds
|
||||
// 1250 = TTN FUP (30 seconds / 24 hours)
|
||||
// if not called, this corresponds to setDutyCycle(true, 0)
|
||||
// setting this to 0 corresponds to the band's maximum allowed dutycycle by law
|
||||
node.setDutyCycle(true, 1250);
|
||||
|
||||
// enable or disable the dwell time limits
|
||||
// the second argument specifies the allowed airtime per uplink in milliseconds
|
||||
// unless specified, this argument is set to 0
|
||||
// setting this to 0 corresponds to the band's maximum allowed dwell time by law
|
||||
node.setDwellTime(true, 400);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
int state = RADIOLIB_ERR_NONE;
|
||||
|
||||
// set battery fill level - the LoRaWAN network server
|
||||
// may periodically request this information
|
||||
// 0 = external power source
|
||||
// 1 = lowest (empty battery)
|
||||
// 254 = highest (full battery)
|
||||
// 255 = unable to measure
|
||||
uint8_t battLevel = 146;
|
||||
node.setDeviceStatus(battLevel);
|
||||
|
||||
// retrieve the last uplink frame counter
|
||||
uint32_t fcntUp = node.getFcntUp();
|
||||
|
||||
Serial.print(F("[LoRaWAN] Sending uplink packet ... "));
|
||||
String strUp = "Hello!" + String(fcntUp);
|
||||
|
||||
// send a confirmed uplink to port 10 every 64th frame
|
||||
// and also request the LinkCheck and DeviceTime MAC commands
|
||||
if(fcntUp % 64 == 0) {
|
||||
node.sendMacCommandReq(RADIOLIB_LORAWAN_MAC_LINK_CHECK);
|
||||
node.sendMacCommandReq(RADIOLIB_LORAWAN_MAC_DEVICE_TIME);
|
||||
state = node.uplink(strUp, 10, true);
|
||||
} else {
|
||||
state = node.uplink(strUp, 10);
|
||||
}
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
// after uplink, you can call downlink(),
|
||||
// to receive any possible reply from the server
|
||||
// this function must be called within a few seconds
|
||||
// after uplink to receive the downlink!
|
||||
Serial.print(F("[LoRaWAN] Waiting for downlink ... "));
|
||||
String strDown;
|
||||
|
||||
// you can also retrieve additional information about
|
||||
// uplink or downlink by passing a reference to
|
||||
// LoRaWANEvent_t structure
|
||||
LoRaWANEvent_t event;
|
||||
state = node.downlink(strDown, &event);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
|
||||
// print data of the packet (if there are any)
|
||||
Serial.print(F("[LoRaWAN] Data:\t\t"));
|
||||
if(strDown.length() > 0) {
|
||||
Serial.println(strDown);
|
||||
} else {
|
||||
Serial.println(F("<MAC commands only>"));
|
||||
}
|
||||
|
||||
// print RSSI (Received Signal Strength Indicator)
|
||||
Serial.print(F("[LoRaWAN] RSSI:\t\t"));
|
||||
Serial.print(radio.getRSSI());
|
||||
Serial.println(F(" dBm"));
|
||||
|
||||
// print SNR (Signal-to-Noise Ratio)
|
||||
Serial.print(F("[LoRaWAN] SNR:\t\t"));
|
||||
Serial.print(radio.getSNR());
|
||||
Serial.println(F(" dB"));
|
||||
|
||||
// print frequency error
|
||||
Serial.print(F("[LoRaWAN] Frequency error:\t"));
|
||||
Serial.print(radio.getFrequencyError());
|
||||
Serial.println(F(" Hz"));
|
||||
|
||||
// print extra information about the event
|
||||
Serial.println(F("[LoRaWAN] Event information:"));
|
||||
Serial.print(F("[LoRaWAN] Direction:\t"));
|
||||
if(event.dir == RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK) {
|
||||
Serial.println(F("uplink"));
|
||||
} else {
|
||||
Serial.println(F("downlink"));
|
||||
}
|
||||
Serial.print(F("[LoRaWAN] Confirmed:\t"));
|
||||
Serial.println(event.confirmed);
|
||||
Serial.print(F("[LoRaWAN] Confirming:\t"));
|
||||
Serial.println(event.confirming);
|
||||
Serial.print(F("[LoRaWAN] Datarate:\t"));
|
||||
Serial.println(event.datarate);
|
||||
Serial.print(F("[LoRaWAN] Frequency:\t"));
|
||||
Serial.print(event.freq, 3);
|
||||
Serial.println(F(" MHz"));
|
||||
Serial.print(F("[LoRaWAN] Output power:\t"));
|
||||
Serial.print(event.power);
|
||||
Serial.println(F(" dBm"));
|
||||
Serial.print(F("[LoRaWAN] Frame count:\t"));
|
||||
Serial.println(event.fcnt);
|
||||
Serial.print(F("[LoRaWAN] Port:\t\t"));
|
||||
Serial.println(event.port);
|
||||
|
||||
Serial.print(radio.getFrequencyError());
|
||||
|
||||
uint8_t margin = 0;
|
||||
uint8_t gwCnt = 0;
|
||||
if(node.getMacLinkCheckAns(&margin, &gwCnt)) {
|
||||
Serial.print(F("[LoRaWAN] LinkCheck margin:\t"));
|
||||
Serial.println(margin);
|
||||
Serial.print(F("[LoRaWAN] LinkCheck count:\t"));
|
||||
Serial.println(gwCnt);
|
||||
}
|
||||
|
||||
uint32_t networkTime = 0;
|
||||
uint8_t fracSecond = 0;
|
||||
if(node.getMacDeviceTimeAns(&networkTime, &fracSecond, true)) {
|
||||
Serial.print(F("[LoRaWAN] DeviceTime Unix:\t"));
|
||||
Serial.println(networkTime);
|
||||
Serial.print(F("[LoRaWAN] LinkCheck second:\t1/"));
|
||||
Serial.println(fracSecond);
|
||||
}
|
||||
|
||||
} else if(state == RADIOLIB_ERR_RX_TIMEOUT) {
|
||||
Serial.println(F("timeout!"));
|
||||
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
// on EEPROM enabled boards, you can save the current session
|
||||
// by calling "saveSession" which allows retrieving the session after reboot or deepsleep
|
||||
/*
|
||||
node.saveSession();
|
||||
*/
|
||||
|
||||
// wait before sending another packet
|
||||
uint32_t minimumDelay = 60000; // try to send once every minute
|
||||
uint32_t interval = node.timeUntilUplink(); // calculate minimum duty cycle delay (per law!)
|
||||
uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows
|
||||
|
||||
delay(delayMs);
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
RadioLib SX127x Morse Receive AM Example
|
||||
|
||||
This example receives Morse code message using
|
||||
SX1278's FSK modem. The signal is expected to be
|
||||
modulated as OOK, to be demodulated in AM mode.
|
||||
|
||||
Other modules that can be used for Morse Code
|
||||
with AFSK modulation:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- Si443x/RFM2x
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create AFSK client instance using the FSK module
|
||||
// pin 5 is connected to SX1278 DIO2
|
||||
AFSKClient audio(&radio, 5);
|
||||
|
||||
// create Morse client instance using the AFSK instance
|
||||
MorseClient morse(&audio);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while (true);
|
||||
}
|
||||
|
||||
// when using one of the non-LoRa modules for Morse code
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
// initialize Morse client
|
||||
Serial.print(F("[Morse] Initializing ... "));
|
||||
// AFSK tone frequency: 400 Hz
|
||||
// speed: 20 words per minute
|
||||
state = morse.begin(400);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// after that, set mode to OOK to emulate AM modulation
|
||||
Serial.print(F("[SX1278] Switching to OOK ... "));
|
||||
state = radio.setOOK(true);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// start direct mode reception
|
||||
radio.receiveDirect();
|
||||
}
|
||||
|
||||
// save symbol and length between loops
|
||||
byte symbol = 0;
|
||||
byte len = 0;
|
||||
|
||||
void loop() {
|
||||
// try to read a new symbol
|
||||
int state = morse.read(&symbol, &len);
|
||||
|
||||
// check if we have something to decode
|
||||
if(state != RADIOLIB_MORSE_INTER_SYMBOL) {
|
||||
// decode and print
|
||||
Serial.print(MorseClient::decode(symbol, len));
|
||||
|
||||
// reset the symbol buffer
|
||||
symbol = 0;
|
||||
len = 0;
|
||||
|
||||
// check if we have a complete word
|
||||
if(state == RADIOLIB_MORSE_WORD_COMPLETE) {
|
||||
// inter-word space, interpret that as a new line
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
/*
|
||||
RadioLib Morse Transmit AM Example
|
||||
|
||||
This example sends Morse code message using
|
||||
SX1278's FSK modem. The signal is modulated
|
||||
as OOK, and may be demodulated in AM mode.
|
||||
|
||||
Other modules that can be used for Morse Code
|
||||
with AM modulation:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- Si443x/RFM2x
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create AFSK client instance using the FSK module
|
||||
// pin 5 is connected to SX1278 DIO2
|
||||
AFSKClient audio(&radio, 5);
|
||||
|
||||
// create Morse client instance using the AFSK instance
|
||||
MorseClient morse(&audio);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules for Morse code
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize Morse client
|
||||
Serial.print(F("[Morse] Initializing ... "));
|
||||
// tone frequency: 400 Hz
|
||||
// speed: 20 words per minute
|
||||
state = morse.begin(400);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// after that, set mode to OOK to emulate AM modulation
|
||||
Serial.print(F("[SX1278] Switching to OOK ... "));
|
||||
state = radio.setOOK(true);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[Morse] Sending Morse data ... "));
|
||||
|
||||
// MorseClient supports all methods of the Serial class
|
||||
// NOTE: Characters that do not have ITU-R M.1677-1
|
||||
// representation will not be sent! Lower case
|
||||
// letters will be capitalized.
|
||||
|
||||
// send start signal first
|
||||
morse.startSignal();
|
||||
|
||||
// Arduino String class
|
||||
String aStr = "Arduino String";
|
||||
morse.print(aStr);
|
||||
|
||||
// character array (C-String)
|
||||
morse.print("C-String");
|
||||
|
||||
// string saved in flash
|
||||
morse.print(F("Flash String"));
|
||||
|
||||
// character
|
||||
morse.print('c');
|
||||
|
||||
// byte
|
||||
// formatting DEC/HEX/OCT/BIN is supported for
|
||||
// any integer type (byte/int/long)
|
||||
morse.print(255, HEX);
|
||||
|
||||
// integer number
|
||||
int i = 1000;
|
||||
morse.print(i);
|
||||
|
||||
// floating point number
|
||||
// NOTE: When using println(), the transmission will be
|
||||
// terminated with end-of-work signal (...-.-).
|
||||
float f = -3.1415;
|
||||
morse.println(f, 3);
|
||||
|
||||
Serial.println(F("done!"));
|
||||
|
||||
// wait for a second before transmitting again
|
||||
delay(1000);
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
RadioLib Morse Transmit AFSK Example
|
||||
|
||||
This example sends Morse code message using
|
||||
SX1278's FSK modem. The signal is modulated
|
||||
as AFSK, and may be demodulated in FM mode.
|
||||
|
||||
Other modules that can be used for Morse Code
|
||||
with AFSK modulation:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- Si443x/RFM2x
|
||||
- SX126x/LLCC68 (only devices without TCXO!)
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create AFSK client instance using the FSK module
|
||||
// this requires connection to the module direct
|
||||
// input pin, here connected to Arduino pin 5
|
||||
// SX127x/RFM9x: DIO2
|
||||
// RF69: DIO2
|
||||
// SX1231: DIO2
|
||||
// CC1101: GDO2
|
||||
// Si443x/RFM2x: GPIO
|
||||
// SX126x/LLCC68: DIO2
|
||||
AFSKClient audio(&radio, 5);
|
||||
|
||||
// create Morse client instance using the AFSK instance
|
||||
MorseClient morse(&audio);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules for Morse code
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize Morse client
|
||||
Serial.print(F("[Morse] Initializing ... "));
|
||||
// tone frequency: 400 Hz
|
||||
// speed: 20 words per minute
|
||||
state = morse.begin(400);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[Morse] Sending Morse data ... "));
|
||||
|
||||
// MorseClient supports all methods of the Serial class
|
||||
// NOTE: Characters that do not have ITU-R M.1677-1
|
||||
// representation will not be sent! Lower case
|
||||
// letters will be capitalized.
|
||||
|
||||
// send start signal first
|
||||
morse.startSignal();
|
||||
|
||||
// Arduino String class
|
||||
String aStr = "Arduino String";
|
||||
morse.print(aStr);
|
||||
|
||||
// character array (C-String)
|
||||
morse.print("C-String");
|
||||
|
||||
// string saved in flash
|
||||
morse.print(F("Flash String"));
|
||||
|
||||
// character
|
||||
morse.print('c');
|
||||
|
||||
// byte
|
||||
// formatting DEC/HEX/OCT/BIN is supported for
|
||||
// any integer type (byte/int/long)
|
||||
morse.print(255, HEX);
|
||||
|
||||
// integer number
|
||||
int i = 1000;
|
||||
morse.print(i);
|
||||
|
||||
// floating point number
|
||||
// NOTE: When using println(), the transmission will be
|
||||
// terminated with end-of-work signal (...-.-).
|
||||
float f = -3.1415;
|
||||
morse.println(f, 3);
|
||||
|
||||
Serial.println(F("done!"));
|
||||
|
||||
// wait for a second before transmitting again
|
||||
delay(1000);
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
RadioLib Morse Transmit SSB Example
|
||||
|
||||
This example sends Morse code message using
|
||||
SX1278's FSK modem. The signal is an unmodulated
|
||||
carrier wave, and may be demodulated in SSB mode.
|
||||
|
||||
Other modules that can be used for Morse Code:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- SX126x
|
||||
- nRF24
|
||||
- Si443x/RFM2x
|
||||
- SX128x
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(10, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create Morse client instance using the FSK module
|
||||
MorseClient morse(&radio);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules for Morse code
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// initialize Morse client
|
||||
Serial.print(F("[Morse] Initializing ... "));
|
||||
// carrier wave frequency: 434.0 MHz
|
||||
// speed: 20 words per minute
|
||||
state = morse.begin(434.0);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[Morse] Sending Morse data ... "));
|
||||
|
||||
// MorseClient supports all methods of the Serial class
|
||||
// NOTE: Characters that do not have ITU-R M.1677-1
|
||||
// representation will not be sent! Lower case
|
||||
// letters will be capitalized.
|
||||
|
||||
// send start signal first
|
||||
morse.startSignal();
|
||||
|
||||
// Arduino String class
|
||||
String aStr = "Arduino String";
|
||||
morse.print(aStr);
|
||||
|
||||
// character array (C-String)
|
||||
morse.print("C-String");
|
||||
|
||||
// string saved in flash
|
||||
morse.print(F("Flash String"));
|
||||
|
||||
// character
|
||||
morse.print('c');
|
||||
|
||||
// byte
|
||||
// formatting DEC/HEX/OCT/BIN is supported for
|
||||
// any integer type (byte/int/long)
|
||||
morse.print(255, HEX);
|
||||
|
||||
// integer number
|
||||
int i = 1000;
|
||||
morse.print(i);
|
||||
|
||||
// floating point number
|
||||
// NOTE: When using println(), the transmission will be
|
||||
// terminated with end-of-work signal (...-.-).
|
||||
float f = -3.1415;
|
||||
morse.println(f, 3);
|
||||
|
||||
Serial.println(F("done!"));
|
||||
|
||||
// wait for a second before transmitting again
|
||||
delay(1000);
|
||||
}
|
3
lib/libesp32/RadioLib/examples/NonArduino/ESP-IDF/.gitignore
vendored
Normal file
3
lib/libesp32/RadioLib/examples/NonArduino/ESP-IDF/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# generated by ESP-IDF
|
||||
managed_components/
|
||||
dependencies.lock
|
@ -0,0 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# include the top-level cmake
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
# name the project something nice
|
||||
project(esp-sx1261)
|
@ -0,0 +1,4 @@
|
||||
# register the component and set "RadioLib", "esp_timer" and "driver" as required
|
||||
idf_component_register(SRCS "main.cpp"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES RadioLib esp_timer driver)
|
322
lib/libesp32/RadioLib/examples/NonArduino/ESP-IDF/main/EspHal.h
Normal file
322
lib/libesp32/RadioLib/examples/NonArduino/ESP-IDF/main/EspHal.h
Normal file
@ -0,0 +1,322 @@
|
||||
#ifndef ESP_HAL_H
|
||||
#define ESP_HAL_H
|
||||
|
||||
// include RadioLib
|
||||
#include <RadioLib.h>
|
||||
|
||||
// this example only works on ESP32 and is unlikely to work on ESP32S2/S3 etc.
|
||||
// if you need high portability, you should probably use Arduino anyway ...
|
||||
#if CONFIG_IDF_TARGET_ESP32 == 0
|
||||
#error Target is not ESP32!
|
||||
#endif
|
||||
|
||||
// include all the dependencies
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp32/rom/gpio.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/spi_reg.h"
|
||||
#include "soc/spi_struct.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
// define Arduino-style macros
|
||||
#define LOW (0x0)
|
||||
#define HIGH (0x1)
|
||||
#define INPUT (0x01)
|
||||
#define OUTPUT (0x03)
|
||||
#define RISING (0x01)
|
||||
#define FALLING (0x02)
|
||||
#define NOP() asm volatile ("nop")
|
||||
|
||||
#define MATRIX_DETACH_OUT_SIG (0x100)
|
||||
#define MATRIX_DETACH_IN_LOW_PIN (0x30)
|
||||
|
||||
// all of the following is needed to calculate SPI clock divider
|
||||
#define ClkRegToFreq(reg) (apb_freq / (((reg)->clkdiv_pre + 1) * ((reg)->clkcnt_n + 1)))
|
||||
|
||||
typedef union {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint32_t clkcnt_l: 6;
|
||||
uint32_t clkcnt_h: 6;
|
||||
uint32_t clkcnt_n: 6;
|
||||
uint32_t clkdiv_pre: 13;
|
||||
uint32_t clk_equ_sysclk: 1;
|
||||
};
|
||||
} spiClk_t;
|
||||
|
||||
uint32_t getApbFrequency() {
|
||||
rtc_cpu_freq_config_t conf;
|
||||
rtc_clk_cpu_freq_get_config(&conf);
|
||||
|
||||
if(conf.freq_mhz >= 80) {
|
||||
return(80 * MHZ);
|
||||
}
|
||||
|
||||
return((conf.source_freq_mhz * MHZ) / conf.div);
|
||||
}
|
||||
|
||||
uint32_t spiFrequencyToClockDiv(uint32_t freq) {
|
||||
uint32_t apb_freq = getApbFrequency();
|
||||
if(freq >= apb_freq) {
|
||||
return SPI_CLK_EQU_SYSCLK;
|
||||
}
|
||||
|
||||
const spiClk_t minFreqReg = { 0x7FFFF000 };
|
||||
uint32_t minFreq = ClkRegToFreq((spiClk_t*) &minFreqReg);
|
||||
if(freq < minFreq) {
|
||||
return minFreqReg.value;
|
||||
}
|
||||
|
||||
uint8_t calN = 1;
|
||||
spiClk_t bestReg = { 0 };
|
||||
int32_t bestFreq = 0;
|
||||
while(calN <= 0x3F) {
|
||||
spiClk_t reg = { 0 };
|
||||
int32_t calFreq;
|
||||
int32_t calPre;
|
||||
int8_t calPreVari = -2;
|
||||
|
||||
reg.clkcnt_n = calN;
|
||||
|
||||
while(calPreVari++ <= 1) {
|
||||
calPre = (((apb_freq / (reg.clkcnt_n + 1)) / freq) - 1) + calPreVari;
|
||||
if(calPre > 0x1FFF) {
|
||||
reg.clkdiv_pre = 0x1FFF;
|
||||
} else if(calPre <= 0) {
|
||||
reg.clkdiv_pre = 0;
|
||||
} else {
|
||||
reg.clkdiv_pre = calPre;
|
||||
}
|
||||
reg.clkcnt_l = ((reg.clkcnt_n + 1) / 2);
|
||||
calFreq = ClkRegToFreq(®);
|
||||
if(calFreq == (int32_t) freq) {
|
||||
memcpy(&bestReg, ®, sizeof(bestReg));
|
||||
break;
|
||||
} else if(calFreq < (int32_t) freq) {
|
||||
if(RADIOLIB_ABS(freq - calFreq) < RADIOLIB_ABS(freq - bestFreq)) {
|
||||
bestFreq = calFreq;
|
||||
memcpy(&bestReg, ®, sizeof(bestReg));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(calFreq == (int32_t) freq) {
|
||||
break;
|
||||
}
|
||||
calN++;
|
||||
}
|
||||
return(bestReg.value);
|
||||
}
|
||||
|
||||
// create a new ESP-IDF hardware abstraction layer
|
||||
// the HAL must inherit from the base RadioLibHal class
|
||||
// and implement all of its virtual methods
|
||||
// this is pretty much just copied from Arduino ESP32 core
|
||||
class EspHal : public RadioLibHal {
|
||||
public:
|
||||
// default constructor - initializes the base HAL and any needed private members
|
||||
EspHal(int8_t sck, int8_t miso, int8_t mosi)
|
||||
: RadioLibHal(INPUT, OUTPUT, LOW, HIGH, RISING, FALLING),
|
||||
spiSCK(sck), spiMISO(miso), spiMOSI(mosi) {
|
||||
}
|
||||
|
||||
void init() override {
|
||||
// we only need to init the SPI here
|
||||
spiBegin();
|
||||
}
|
||||
|
||||
void term() override {
|
||||
// we only need to stop the SPI here
|
||||
spiEnd();
|
||||
}
|
||||
|
||||
// GPIO-related methods (pinMode, digitalWrite etc.) should check
|
||||
// RADIOLIB_NC as an alias for non-connected pins
|
||||
void pinMode(uint32_t pin, uint32_t mode) override {
|
||||
if(pin == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_hal_context_t gpiohal;
|
||||
gpiohal.dev = GPIO_LL_GET_HW(GPIO_PORT_0);
|
||||
|
||||
gpio_config_t conf = {
|
||||
.pin_bit_mask = (1ULL<<pin),
|
||||
.mode = (gpio_mode_t)mode,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = (gpio_int_type_t)gpiohal.dev->pin[pin].int_type,
|
||||
};
|
||||
gpio_config(&conf);
|
||||
}
|
||||
|
||||
void digitalWrite(uint32_t pin, uint32_t value) override {
|
||||
if(pin == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_set_level((gpio_num_t)pin, value);
|
||||
}
|
||||
|
||||
uint32_t digitalRead(uint32_t pin) override {
|
||||
if(pin == RADIOLIB_NC) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(gpio_get_level((gpio_num_t)pin));
|
||||
}
|
||||
|
||||
void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override {
|
||||
if(interruptNum == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_install_isr_service((int)ESP_INTR_FLAG_IRAM);
|
||||
gpio_set_intr_type((gpio_num_t)interruptNum, (gpio_int_type_t)(mode & 0x7));
|
||||
|
||||
// this uses function typecasting, which is not defined when the functions have different signatures
|
||||
// untested and might not work
|
||||
gpio_isr_handler_add((gpio_num_t)interruptNum, (void (*)(void*))interruptCb, NULL);
|
||||
}
|
||||
|
||||
void detachInterrupt(uint32_t interruptNum) override {
|
||||
if(interruptNum == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_isr_handler_remove((gpio_num_t)interruptNum);
|
||||
gpio_wakeup_disable((gpio_num_t)interruptNum);
|
||||
gpio_set_intr_type((gpio_num_t)interruptNum, GPIO_INTR_DISABLE);
|
||||
}
|
||||
|
||||
void delay(unsigned long ms) override {
|
||||
vTaskDelay(ms / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
void delayMicroseconds(unsigned long us) override {
|
||||
uint64_t m = (uint64_t)esp_timer_get_time();
|
||||
if(us) {
|
||||
uint64_t e = (m + us);
|
||||
if(m > e) { // overflow
|
||||
while((uint64_t)esp_timer_get_time() > e) {
|
||||
NOP();
|
||||
}
|
||||
}
|
||||
while((uint64_t)esp_timer_get_time() < e) {
|
||||
NOP();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long millis() override {
|
||||
return((unsigned long)(esp_timer_get_time() / 1000ULL));
|
||||
}
|
||||
|
||||
unsigned long micros() override {
|
||||
return((unsigned long)(esp_timer_get_time()));
|
||||
}
|
||||
|
||||
long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override {
|
||||
if(pin == RADIOLIB_NC) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
this->pinMode(pin, INPUT);
|
||||
uint32_t start = this->micros();
|
||||
uint32_t curtick = this->micros();
|
||||
|
||||
while(this->digitalRead(pin) == state) {
|
||||
if((this->micros() - curtick) > timeout) {
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
return(this->micros() - start);
|
||||
}
|
||||
|
||||
void spiBegin() {
|
||||
// enable peripheral
|
||||
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI2_CLK_EN);
|
||||
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI2_RST);
|
||||
|
||||
// reset the control struct
|
||||
this->spi->slave.trans_done = 0;
|
||||
this->spi->slave.val = 0;
|
||||
this->spi->pin.val = 0;
|
||||
this->spi->user.val = 0;
|
||||
this->spi->user1.val = 0;
|
||||
this->spi->ctrl.val = 0;
|
||||
this->spi->ctrl1.val = 0;
|
||||
this->spi->ctrl2.val = 0;
|
||||
this->spi->clock.val = 0;
|
||||
this->spi->user.usr_mosi = 1;
|
||||
this->spi->user.usr_miso = 1;
|
||||
this->spi->user.doutdin = 1;
|
||||
for(uint8_t i = 0; i < 16; i++) {
|
||||
this->spi->data_buf[i] = 0x00000000;
|
||||
}
|
||||
|
||||
// set SPI mode 0
|
||||
this->spi->pin.ck_idle_edge = 0;
|
||||
this->spi->user.ck_out_edge = 0;
|
||||
|
||||
// set bit order to MSB first
|
||||
this->spi->ctrl.wr_bit_order = 0;
|
||||
this->spi->ctrl.rd_bit_order = 0;
|
||||
|
||||
// set the clock
|
||||
this->spi->clock.val = spiFrequencyToClockDiv(2000000);
|
||||
|
||||
// initialize pins
|
||||
this->pinMode(this->spiSCK, OUTPUT);
|
||||
this->pinMode(this->spiMISO, INPUT);
|
||||
this->pinMode(this->spiMOSI, OUTPUT);
|
||||
gpio_matrix_out(this->spiSCK, HSPICLK_OUT_IDX, false, false);
|
||||
gpio_matrix_in(this->spiMISO, HSPIQ_OUT_IDX, false);
|
||||
gpio_matrix_out(this->spiMOSI, HSPID_IN_IDX, false, false);
|
||||
}
|
||||
|
||||
void spiBeginTransaction() {
|
||||
// not needed - in ESP32 Arduino core, this function
|
||||
// repeats clock div, mode and bit order configuration
|
||||
}
|
||||
|
||||
uint8_t spiTransferByte(uint8_t b) {
|
||||
this->spi->mosi_dlen.usr_mosi_dbitlen = 7;
|
||||
this->spi->miso_dlen.usr_miso_dbitlen = 7;
|
||||
this->spi->data_buf[0] = b;
|
||||
this->spi->cmd.usr = 1;
|
||||
while(this->spi->cmd.usr);
|
||||
return(this->spi->data_buf[0] & 0xFF);
|
||||
}
|
||||
|
||||
void spiTransfer(uint8_t* out, size_t len, uint8_t* in) {
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
in[i] = this->spiTransferByte(out[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void spiEndTransaction() {
|
||||
// nothing needs to be done here
|
||||
}
|
||||
|
||||
void spiEnd() {
|
||||
// detach pins
|
||||
gpio_matrix_out(this->spiSCK, MATRIX_DETACH_OUT_SIG, false, false);
|
||||
gpio_matrix_in(this->spiMISO, MATRIX_DETACH_IN_LOW_PIN, false);
|
||||
gpio_matrix_out(this->spiMOSI, MATRIX_DETACH_OUT_SIG, false, false);
|
||||
}
|
||||
|
||||
private:
|
||||
// the HAL can contain any additional private members
|
||||
int8_t spiSCK;
|
||||
int8_t spiMISO;
|
||||
int8_t spiMOSI;
|
||||
spi_dev_t * spi = (volatile spi_dev_t *)(DR_REG_SPI2_BASE);
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,7 @@
|
||||
dependencies:
|
||||
RadioLib:
|
||||
# referenced locally because the example is a part of the repository itself
|
||||
# under normal circumstances, it's preferrable to reference the repository instead
|
||||
# for other options, see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html
|
||||
path: ../../../../../RadioLib
|
||||
#git: https://github.com/jgromes/RadioLib.git
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
RadioLib Non-Arduino ESP-IDF Example
|
||||
|
||||
This example shows how to use RadioLib without Arduino.
|
||||
In this case, a Liligo T-BEAM (ESP32 and SX1276)
|
||||
is used.
|
||||
|
||||
Can be used as a starting point to port RadioLib to any platform!
|
||||
See this API reference page for details on the RadioLib hardware abstraction
|
||||
https://jgromes.github.io/RadioLib/class_hal.html
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// include the hardware abstraction layer
|
||||
#include "EspHal.h"
|
||||
|
||||
// create a new instance of the HAL class
|
||||
EspHal* hal = new EspHal(5, 19, 27);
|
||||
|
||||
// now we can create the radio module
|
||||
// NSS pin: 18
|
||||
// DIO0 pin: 26
|
||||
// NRST pin: 14
|
||||
// DIO1 pin: 33
|
||||
SX1276 radio = new Module(hal, 18, 26, 14, 33);
|
||||
|
||||
static const char *TAG = "main";
|
||||
|
||||
// the entry point for the program
|
||||
// it must be declared as "extern C" because the compiler assumes this will be a C function
|
||||
extern "C" void app_main(void) {
|
||||
// initialize just like with Arduino
|
||||
ESP_LOGI(TAG, "[SX1276] Initializing ... ");
|
||||
int state = radio.begin();
|
||||
if (state != RADIOLIB_ERR_NONE) {
|
||||
ESP_LOGI(TAG, "failed, code %d\n", state);
|
||||
while(true) {
|
||||
hal->delay(1000);
|
||||
}
|
||||
}
|
||||
ESP_LOGI(TAG, "success!\n");
|
||||
|
||||
// loop forever
|
||||
for(;;) {
|
||||
// send a packet
|
||||
ESP_LOGI(TAG, "[SX1276] Transmitting packet ... ");
|
||||
state = radio.transmit("Hello World!");
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
// the packet was successfully transmitted
|
||||
ESP_LOGI(TAG, "success!");
|
||||
|
||||
} else {
|
||||
ESP_LOGI(TAG, "failed, code %d\n", state);
|
||||
|
||||
}
|
||||
|
||||
// wait for a second before transmitting again
|
||||
hal->delay(1000);
|
||||
|
||||
}
|
||||
|
||||
}
|
1669
lib/libesp32/RadioLib/examples/NonArduino/ESP-IDF/sdkconfig
Normal file
1669
lib/libesp32/RadioLib/examples/NonArduino/ESP-IDF/sdkconfig
Normal file
File diff suppressed because it is too large
Load Diff
1
lib/libesp32/RadioLib/examples/NonArduino/Pico/.gitignore
vendored
Normal file
1
lib/libesp32/RadioLib/examples/NonArduino/Pico/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
build/
|
@ -0,0 +1,33 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
|
||||
# Pull in SDK (must be before project)
|
||||
include(pico_sdk_import.cmake)
|
||||
|
||||
project(pico-sx1276 C CXX ASM)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# Initialize the SDK
|
||||
pico_sdk_init()
|
||||
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wno-format
|
||||
-Wno-unused-function
|
||||
)
|
||||
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../../../../RadioLib" "${CMAKE_CURRENT_BINARY_DIR}/RadioLib")
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
main.cpp
|
||||
)
|
||||
|
||||
# Pull in common dependencies
|
||||
target_link_libraries(${PROJECT_NAME} pico_stdlib hardware_spi hardware_gpio hardware_timer RadioLib)
|
||||
|
||||
|
||||
pico_enable_stdio_usb(${PROJECT_NAME} 1)
|
||||
pico_enable_stdio_uart(${PROJECT_NAME} 0)
|
||||
|
||||
# Create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(${PROJECT_NAME})
|
143
lib/libesp32/RadioLib/examples/NonArduino/Pico/PicoHal.h
Normal file
143
lib/libesp32/RadioLib/examples/NonArduino/Pico/PicoHal.h
Normal file
@ -0,0 +1,143 @@
|
||||
#ifndef PICO_HAL_H
|
||||
#define PICO_HAL_H
|
||||
|
||||
// include RadioLib
|
||||
#include <RadioLib.h>
|
||||
|
||||
// include the necessary Pico libraries
|
||||
#include <pico/stdlib.h>
|
||||
#include "hardware/spi.h"
|
||||
#include "hardware/timer.h"
|
||||
|
||||
// create a new Raspberry Pi Pico hardware abstraction
|
||||
// layer using the Pico SDK
|
||||
// the HAL must inherit from the base RadioLibHal class
|
||||
// and implement all of its virtual methods
|
||||
class PicoHal : public RadioLibHal {
|
||||
public:
|
||||
PicoHal(spi_inst_t *spiChannel, uint32_t misoPin, uint32_t mosiPin, uint32_t sckPin, uint32_t spiSpeed = 500 * 1000)
|
||||
: RadioLibHal(GPIO_IN, GPIO_OUT, 0, 1, GPIO_IRQ_EDGE_RISE, GPIO_IRQ_EDGE_FALL),
|
||||
_spiChannel(spiChannel),
|
||||
_spiSpeed(spiSpeed),
|
||||
_misoPin(misoPin),
|
||||
_mosiPin(mosiPin),
|
||||
_sckPin(sckPin) {
|
||||
}
|
||||
|
||||
void init() override {
|
||||
stdio_init_all();
|
||||
spiBegin();
|
||||
}
|
||||
|
||||
void term() override {
|
||||
spiEnd();
|
||||
}
|
||||
|
||||
// GPIO-related methods (pinMode, digitalWrite etc.) should check
|
||||
// RADIOLIB_NC as an alias for non-connected pins
|
||||
void pinMode(uint32_t pin, uint32_t mode) override {
|
||||
if (pin == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_init(pin);
|
||||
gpio_set_dir(pin, mode);
|
||||
}
|
||||
|
||||
void digitalWrite(uint32_t pin, uint32_t value) override {
|
||||
if (pin == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_put(pin, (bool)value);
|
||||
}
|
||||
|
||||
uint32_t digitalRead(uint32_t pin) override {
|
||||
if (pin == RADIOLIB_NC) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return gpio_get(pin);
|
||||
}
|
||||
|
||||
void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override {
|
||||
if (interruptNum == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_set_irq_enabled_with_callback(interruptNum, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, (gpio_irq_callback_t)interruptCb);
|
||||
}
|
||||
|
||||
void detachInterrupt(uint32_t interruptNum) override {
|
||||
if (interruptNum == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_set_irq_enabled_with_callback(interruptNum, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false, NULL);
|
||||
}
|
||||
|
||||
void delay(unsigned long ms) override {
|
||||
sleep_ms(ms);
|
||||
}
|
||||
|
||||
void delayMicroseconds(unsigned long us) override {
|
||||
sleep_us(us);
|
||||
}
|
||||
|
||||
unsigned long millis() override {
|
||||
return to_ms_since_boot(get_absolute_time());
|
||||
}
|
||||
|
||||
unsigned long micros() override {
|
||||
return to_us_since_boot(get_absolute_time());
|
||||
}
|
||||
|
||||
long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override {
|
||||
if (pin == RADIOLIB_NC) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
this->pinMode(pin, GPIO_IN);
|
||||
uint32_t start = this->micros();
|
||||
uint32_t curtick = this->micros();
|
||||
|
||||
while (this->digitalRead(pin) == state) {
|
||||
if ((this->micros() - curtick) > timeout) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return (this->micros() - start);
|
||||
}
|
||||
|
||||
void spiBegin() {
|
||||
spi_init(_spiChannel, _spiSpeed);
|
||||
spi_set_format(_spiChannel, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
|
||||
|
||||
gpio_set_function(_sckPin, GPIO_FUNC_SPI);
|
||||
gpio_set_function(_mosiPin, GPIO_FUNC_SPI);
|
||||
gpio_set_function(_misoPin, GPIO_FUNC_SPI);
|
||||
}
|
||||
|
||||
void spiBeginTransaction() {}
|
||||
|
||||
void spiTransfer(uint8_t *out, size_t len, uint8_t *in) {
|
||||
spi_write_read_blocking(_spiChannel, out, in, len);
|
||||
}
|
||||
|
||||
void spiEndTransaction() {}
|
||||
|
||||
void spiEnd() {
|
||||
spi_deinit(_spiChannel);
|
||||
}
|
||||
|
||||
private:
|
||||
// the HAL can contain any additional private members
|
||||
spi_inst_t *_spiChannel;
|
||||
uint32_t _spiSpeed;
|
||||
uint32_t _misoPin;
|
||||
uint32_t _mosiPin;
|
||||
uint32_t _sckPin;
|
||||
};
|
||||
|
||||
#endif
|
8
lib/libesp32/RadioLib/examples/NonArduino/Pico/build.sh
Normal file
8
lib/libesp32/RadioLib/examples/NonArduino/Pico/build.sh
Normal file
@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
cd ..
|
3
lib/libesp32/RadioLib/examples/NonArduino/Pico/clean.sh
Normal file
3
lib/libesp32/RadioLib/examples/NonArduino/Pico/clean.sh
Normal file
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
rm -rf ./build
|
86
lib/libesp32/RadioLib/examples/NonArduino/Pico/main.cpp
Normal file
86
lib/libesp32/RadioLib/examples/NonArduino/Pico/main.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
RadioLib Non-Arduino Raspberry Pi Pico library example
|
||||
|
||||
Licensed under the MIT License
|
||||
|
||||
Copyright (c) 2024 Cameron Goddard
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
// define pins to be used
|
||||
#define SPI_PORT spi0
|
||||
#define SPI_MISO 4
|
||||
#define SPI_MOSI 3
|
||||
#define SPI_SCK 2
|
||||
|
||||
#define RFM_NSS 26
|
||||
#define RFM_RST 22
|
||||
#define RFM_DIO0 14
|
||||
#define RFM_DIO1 15
|
||||
|
||||
#include <pico/stdlib.h>
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// include the hardware abstraction layer
|
||||
#include "PicoHal.h"
|
||||
|
||||
// create a new instance of the HAL class
|
||||
PicoHal* hal = new PicoHal(SPI_PORT, SPI_MISO, SPI_MOSI, SPI_SCK);
|
||||
|
||||
// now we can create the radio module
|
||||
// NSS pin: 26
|
||||
// DIO0 pin: 14
|
||||
// RESET pin: 22
|
||||
// DIO1 pin: 15
|
||||
SX1276 radio = new Module(hal, RFM_NSS, RFM_DIO0, RFM_RST, RFM_DIO1);
|
||||
|
||||
int main() {
|
||||
// initialize just like with Arduino
|
||||
printf("[SX1276] Initializing ... ");
|
||||
int state = radio.begin();
|
||||
if (state != RADIOLIB_ERR_NONE) {
|
||||
printf("failed, code %d\n", state);
|
||||
return(1);
|
||||
}
|
||||
printf("success!\n");
|
||||
|
||||
// loop forever
|
||||
for(;;) {
|
||||
// send a packet
|
||||
printf("[SX1276] Transmitting packet ... ");
|
||||
state = radio.transmit("Hello World!");
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
// the packet was successfully transmitted
|
||||
printf("success!\n");
|
||||
|
||||
// wait for a second before transmitting again
|
||||
hal->delay(1000);
|
||||
|
||||
} else {
|
||||
printf("failed, code %d\n", state);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||
|
||||
# This can be dropped into an external project to help locate this SDK
|
||||
# It should be include()ed prior to project()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||
|
||||
if (NOT PICO_SDK_PATH)
|
||||
if (PICO_SDK_FETCH_FROM_GIT)
|
||||
include(FetchContent)
|
||||
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||
endif ()
|
||||
# GIT_SUBMODULES_RECURSE was added in 3.17
|
||||
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG master
|
||||
GIT_SUBMODULES_RECURSE FALSE
|
||||
)
|
||||
else ()
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG master
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (NOT pico_sdk)
|
||||
message("Downloading Raspberry Pi Pico SDK")
|
||||
FetchContent_Populate(pico_sdk)
|
||||
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||
endif ()
|
||||
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||
else ()
|
||||
message(FATAL_ERROR
|
||||
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||
|
||||
include(${PICO_SDK_INIT_CMAKE_FILE})
|
1
lib/libesp32/RadioLib/examples/NonArduino/Raspberry/.gitignore
vendored
Normal file
1
lib/libesp32/RadioLib/examples/NonArduino/Raspberry/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
build/
|
@ -0,0 +1,21 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
|
||||
# create the project
|
||||
project(rpi-sx1261)
|
||||
|
||||
# when using debuggers such as gdb, the following line can be used
|
||||
#set(CMAKE_BUILD_TYPE Debug)
|
||||
|
||||
# if you did not build RadioLib as shared library (see wiki),
|
||||
# you will have to add it as source directory
|
||||
# the following is just an example, yours will likely be different
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../../../../RadioLib" "${CMAKE_CURRENT_BINARY_DIR}/RadioLib")
|
||||
|
||||
# add the executable
|
||||
add_executable(${PROJECT_NAME} main.cpp)
|
||||
|
||||
# link both libraries
|
||||
target_link_libraries(${PROJECT_NAME} RadioLib pigpio)
|
||||
|
||||
# you can also specify RadioLib compile-time flags here
|
||||
#target_compile_definitions(${PROJECT_NAME} PUBLIC RADIOLIB_DEBUG RADIOLIB_VERBOSE)
|
151
lib/libesp32/RadioLib/examples/NonArduino/Raspberry/PiHal.h
Normal file
151
lib/libesp32/RadioLib/examples/NonArduino/Raspberry/PiHal.h
Normal file
@ -0,0 +1,151 @@
|
||||
#ifndef PI_HAL_H
|
||||
#define PI_HAL_H
|
||||
|
||||
// include RadioLib
|
||||
#include <RadioLib.h>
|
||||
|
||||
// include the library for Raspberry GPIO pins
|
||||
#include "pigpio.h"
|
||||
|
||||
// create a new Raspberry Pi hardware abstraction layer
|
||||
// using the pigpio library
|
||||
// the HAL must inherit from the base RadioLibHal class
|
||||
// and implement all of its virtual methods
|
||||
class PiHal : public RadioLibHal {
|
||||
public:
|
||||
// default constructor - initializes the base HAL and any needed private members
|
||||
PiHal(uint8_t spiChannel, uint32_t spiSpeed = 2000000)
|
||||
: RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, RISING_EDGE, FALLING_EDGE),
|
||||
_spiChannel(spiChannel),
|
||||
_spiSpeed(spiSpeed) {
|
||||
}
|
||||
|
||||
void init() override {
|
||||
// first initialise pigpio library
|
||||
gpioInitialise();
|
||||
|
||||
// now the SPI
|
||||
spiBegin();
|
||||
|
||||
// Waveshare LoRaWAN Hat also needs pin 18 to be pulled high to enable the radio
|
||||
gpioSetMode(18, PI_OUTPUT);
|
||||
gpioWrite(18, PI_HIGH);
|
||||
}
|
||||
|
||||
void term() override {
|
||||
// stop the SPI
|
||||
spiEnd();
|
||||
|
||||
// pull the enable pin low
|
||||
gpioSetMode(18, PI_OUTPUT);
|
||||
gpioWrite(18, PI_LOW);
|
||||
|
||||
// finally, stop the pigpio library
|
||||
gpioTerminate();
|
||||
}
|
||||
|
||||
// GPIO-related methods (pinMode, digitalWrite etc.) should check
|
||||
// RADIOLIB_NC as an alias for non-connected pins
|
||||
void pinMode(uint32_t pin, uint32_t mode) override {
|
||||
if(pin == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpioSetMode(pin, mode);
|
||||
}
|
||||
|
||||
void digitalWrite(uint32_t pin, uint32_t value) override {
|
||||
if(pin == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpioWrite(pin, value);
|
||||
}
|
||||
|
||||
uint32_t digitalRead(uint32_t pin) override {
|
||||
if(pin == RADIOLIB_NC) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(gpioRead(pin));
|
||||
}
|
||||
|
||||
void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override {
|
||||
if(interruptNum == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpioSetISRFunc(interruptNum, mode, 0, (gpioISRFunc_t)interruptCb);
|
||||
}
|
||||
|
||||
void detachInterrupt(uint32_t interruptNum) override {
|
||||
if(interruptNum == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpioSetISRFunc(interruptNum, 0, 0, NULL);
|
||||
}
|
||||
|
||||
void delay(unsigned long ms) override {
|
||||
gpioDelay(ms * 1000);
|
||||
}
|
||||
|
||||
void delayMicroseconds(unsigned long us) override {
|
||||
gpioDelay(us);
|
||||
}
|
||||
|
||||
unsigned long millis() override {
|
||||
return(gpioTick() / 1000);
|
||||
}
|
||||
|
||||
unsigned long micros() override {
|
||||
return(gpioTick());
|
||||
}
|
||||
|
||||
long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override {
|
||||
if(pin == RADIOLIB_NC) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
this->pinMode(pin, PI_INPUT);
|
||||
uint32_t start = this->micros();
|
||||
uint32_t curtick = this->micros();
|
||||
|
||||
while(this->digitalRead(pin) == state) {
|
||||
if((this->micros() - curtick) > timeout) {
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
return(this->micros() - start);
|
||||
}
|
||||
|
||||
void spiBegin() {
|
||||
if(_spiHandle < 0) {
|
||||
_spiHandle = spiOpen(_spiChannel, _spiSpeed, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void spiBeginTransaction() {}
|
||||
|
||||
void spiTransfer(uint8_t* out, size_t len, uint8_t* in) {
|
||||
spiXfer(_spiHandle, (char*)out, (char*)in, len);
|
||||
}
|
||||
|
||||
void spiEndTransaction() {}
|
||||
|
||||
void spiEnd() {
|
||||
if(_spiHandle >= 0) {
|
||||
spiClose(_spiHandle);
|
||||
_spiHandle = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// the HAL can contain any additional private members
|
||||
const unsigned int _spiSpeed;
|
||||
const uint8_t _spiChannel;
|
||||
int _spiHandle = -1;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake -G "CodeBlocks - Unix Makefiles" ..
|
||||
make
|
||||
cd ..
|
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
rm -rf ./build
|
66
lib/libesp32/RadioLib/examples/NonArduino/Raspberry/main.cpp
Normal file
66
lib/libesp32/RadioLib/examples/NonArduino/Raspberry/main.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
RadioLib Non-Arduino Raspberry Pi Example
|
||||
|
||||
This example shows how to use RadioLib without Arduino.
|
||||
In this case, a Raspberry Pi with WaveShare SX1302 LoRaWAN Hat
|
||||
using the pigpio library.
|
||||
|
||||
Can be used as a starting point to port RadioLib to any platform!
|
||||
See this API reference page for details on the RadioLib hardware abstraction
|
||||
https://jgromes.github.io/RadioLib/class_hal.html
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// include the hardware abstraction layer
|
||||
#include "PiHal.h"
|
||||
|
||||
// create a new instance of the HAL class
|
||||
// use SPI channel 1, because on Waveshare LoRaWAN Hat,
|
||||
// the SX1261 CS is connected to CE1
|
||||
PiHal* hal = new PiHal(1);
|
||||
|
||||
// now we can create the radio module
|
||||
// pinout corresponds to the Waveshare LoRaWAN Hat
|
||||
// NSS pin: 7
|
||||
// DIO1 pin: 17
|
||||
// NRST pin: 22
|
||||
// BUSY pin: not connected
|
||||
SX1261 radio = new Module(hal, 7, 17, 22, RADIOLIB_NC);
|
||||
|
||||
// the entry point for the program
|
||||
int main(int argc, char** argv) {
|
||||
// initialize just like with Arduino
|
||||
printf("[SX1261] Initializing ... ");
|
||||
int state = radio.begin();
|
||||
if (state != RADIOLIB_ERR_NONE) {
|
||||
printf("failed, code %d\n", state);
|
||||
return(1);
|
||||
}
|
||||
printf("success!\n");
|
||||
|
||||
// loop forever
|
||||
for(;;) {
|
||||
// send a packet
|
||||
printf("[SX1261] Transmitting packet ... ");
|
||||
state = radio.transmit("Hello World!");
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
// the packet was successfully transmitted
|
||||
printf("success!\n");
|
||||
|
||||
// wait for a second before transmitting again
|
||||
hal->delay(1000);
|
||||
|
||||
} else {
|
||||
printf("failed, code %d\n", state);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
2
lib/libesp32/RadioLib/examples/NonArduino/Tock/.gitignore
vendored
Normal file
2
lib/libesp32/RadioLib/examples/NonArduino/Tock/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
build/
|
||||
TockApp.tab
|
@ -0,0 +1,61 @@
|
||||
# RadioLib Non-Arduino Tock Library CMake script
|
||||
#
|
||||
# Licensed under the MIT License
|
||||
#
|
||||
# Copyright (c) 2023 Alistair Francis <alistair@alistair23.me>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
|
||||
# create the project
|
||||
project(tock-sx1261)
|
||||
|
||||
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/libtock-c/userland_generic.ld)
|
||||
|
||||
include("tock.cmake")
|
||||
|
||||
# when using debuggers such as gdb, the following line can be used
|
||||
#set(CMAKE_BUILD_TYPE Debug)
|
||||
|
||||
# if you did not build RadioLib as shared library (see wiki),
|
||||
# you will have to add it as source directory
|
||||
# the following is just an example, yours will likely be different
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../../../../RadioLib" "${CMAKE_CURRENT_BINARY_DIR}/RadioLib")
|
||||
|
||||
# add the executable
|
||||
add_executable(${PROJECT_NAME} main.cpp)
|
||||
|
||||
# link with RadioLib and libtock-c
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||
RadioLib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libtock-c/libtock/build/cortex-m4/libtock.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libtock-c/libc++/cortex-m/libgcc.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libtock-c/libc++/cortex-m/libstdc++.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libtock-c/newlib/cortex-m/v7-m/libc.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libtock-c/newlib/cortex-m/v7-m/libm.a
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libtock-c
|
||||
)
|
||||
|
||||
# you can also specify RadioLib compile-time flags here
|
||||
#target_compile_definitions(${PROJECT_NAME} PUBLIC RADIOLIB_DEBUG RADIOLIB_VERBOSE)
|
28
lib/libesp32/RadioLib/examples/NonArduino/Tock/README.md
Normal file
28
lib/libesp32/RadioLib/examples/NonArduino/Tock/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# RadioLib as Tock application
|
||||
|
||||
[Tock](https://github.com/tock/tock) is an embedded operating system designed
|
||||
for running multiple concurrent, mutually distrustful applications on Cortex-M
|
||||
and RISC-V based embedded platforms.
|
||||
|
||||
RadioLib can be built as a Tock application using
|
||||
[libtock-c](https://github.com/tock/libtock-c). This is an example of running
|
||||
RadioLib as a Tock application.
|
||||
|
||||
This has been tested on the
|
||||
[SparkFun LoRa Thing Plus - expLoRaBLE board] (https://github.com/tock/tock/tree/master/boards/apollo3/lora_things_plus)
|
||||
but will work on any LoRa compatible Tock board (currently only the
|
||||
expLoRaBLE board).
|
||||
|
||||
The RadioLib example can be built with:
|
||||
|
||||
```shell
|
||||
$ git clone https://github.com/jgromes/RadioLib.git
|
||||
$ cd RadioLib/examples/NonArduino/Tock/
|
||||
$ ./build.sh
|
||||
```
|
||||
|
||||
Then in the Tock repo you can flash the kernel and app with:
|
||||
|
||||
```shell
|
||||
$ make flash; APP=RadioLib/examples/NonArduino/Tock/build/tock-sx1261.tbf make flash-app
|
||||
```
|
20
lib/libesp32/RadioLib/examples/NonArduino/Tock/build.sh
Normal file
20
lib/libesp32/RadioLib/examples/NonArduino/Tock/build.sh
Normal file
@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
rm -rf ./build
|
||||
|
||||
cd libtock-c/libtock
|
||||
make -j4
|
||||
cd ../../
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
cmake -G "CodeBlocks - Unix Makefiles" ..
|
||||
make -j4
|
||||
|
||||
cd ..
|
||||
|
||||
elf2tab -n radio-lib --stack 4096 --app-heap 2048 --kernel-heap 2048 \
|
||||
--kernel-major 2 --kernel-minor 1 \
|
||||
-v ./build/tock-sx1261
|
55
lib/libesp32/RadioLib/examples/NonArduino/Tock/libtock-c/.github/workflows/ci.yml
vendored
Normal file
55
lib/libesp32/RadioLib/examples/NonArduino/Tock/libtock-c/.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
name: ci
|
||||
env:
|
||||
TERM: xterm # Makes tput work in actions output
|
||||
|
||||
# Controls when the action will run. Triggers the workflow on push or pull request
|
||||
# events but only for the master branch
|
||||
on:
|
||||
push:
|
||||
branches-ignore: [ staging.tmp, trying.tmp ] # Run CI for all branches except bors tmp branches
|
||||
pull_request: # Run CI for PRs on any branch
|
||||
|
||||
jobs:
|
||||
ci-format:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: false # LVGL makefile manually installs the submodule
|
||||
- uses: fiam/arm-none-eabi-gcc@v1
|
||||
with:
|
||||
release: '10-2020-q4' # The arm-none-eabi-gcc release to use.
|
||||
- name: setup-riscv-toolchain
|
||||
run: |
|
||||
pushd $HOME; wget http://cs.virginia.edu/~bjc8c/archive/gcc-riscv64-unknown-elf-8.3.0-ubuntu.zip; unzip gcc-riscv64-unknown-elf-8.3.0-ubuntu.zip; echo "$HOME/gcc-riscv64-unknown-elf-8.3.0-ubuntu/bin" >> $GITHUB_PATH; popd
|
||||
- name: ci-format
|
||||
run: pushd examples; ./format_all.sh || exit; popd
|
||||
|
||||
ci-build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: fiam/arm-none-eabi-gcc@v1
|
||||
with:
|
||||
release: '10-2020-q4' # The arm-none-eabi-gcc release to use.
|
||||
- name: setup-riscv-toolchain
|
||||
run: |
|
||||
pushd $HOME; wget http://cs.virginia.edu/~bjc8c/archive/gcc-riscv64-unknown-elf-8.3.0-ubuntu.zip; unzip gcc-riscv64-unknown-elf-8.3.0-ubuntu.zip; echo "$HOME/gcc-riscv64-unknown-elf-8.3.0-ubuntu/bin" >> $GITHUB_PATH; popd
|
||||
- name: ci-build
|
||||
run: pushd examples; RISCV=1 ./build_all.sh || exit; popd
|
||||
- name: ci-debug-build
|
||||
run: pushd examples/blink; make debug RAM_START=0x20004000 FLASH_INIT=0x30051 || exit; popd
|
2
lib/libesp32/RadioLib/examples/NonArduino/Tock/libtock-c/.gitignore
vendored
Normal file
2
lib/libesp32/RadioLib/examples/NonArduino/Tock/libtock-c/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
build/
|
||||
.vscode/
|
6
lib/libesp32/RadioLib/examples/NonArduino/Tock/libtock-c/.gitmodules
vendored
Normal file
6
lib/libesp32/RadioLib/examples/NonArduino/Tock/libtock-c/.gitmodules
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
[submodule "lua53/lua"]
|
||||
path = lua53/lua
|
||||
url = https://github.com/lua/lua.git
|
||||
[submodule "lvgl/lvgl"]
|
||||
path = lvgl/lvgl
|
||||
url = https://github.com/littlevgl/lvgl.git
|
@ -0,0 +1,380 @@
|
||||
################################################################################
|
||||
##
|
||||
## libtock-c main build system Makefile.
|
||||
##
|
||||
## Individual applications use this Makefile to invoke the compiler and create
|
||||
## Tock apps.
|
||||
##
|
||||
## This Makefile works by generating at runtime the rules necessary to build the
|
||||
## libtock-c app for all desired architectures, then running each of those rules
|
||||
## to create the compiled output. Each architecture-specific compiled binary is
|
||||
## then combined into a single Tock TAB file.
|
||||
##
|
||||
################################################################################
|
||||
|
||||
# The first target Make finds is its default. So this line needs to be first to
|
||||
# specify `all` as our default rule
|
||||
all:
|
||||
|
||||
# Directory for built output.
|
||||
BUILDDIR ?= build
|
||||
|
||||
# Build settings.
|
||||
include $(TOCK_USERLAND_BASE_DIR)/Configuration.mk
|
||||
|
||||
# Helper functions.
|
||||
include $(TOCK_USERLAND_BASE_DIR)/Helpers.mk
|
||||
|
||||
# Include the libtock makefile. Adds rules that will rebuild the core libtock
|
||||
# library when needed.
|
||||
include $(TOCK_USERLAND_BASE_DIR)/libtock/Makefile
|
||||
|
||||
# Connection to the Tock kernel. Apps need the ability to be loaded onto a
|
||||
# board, and that method is board-specific. So for now, we have the TOCK_BOARD
|
||||
# variable which selects one and we include the appropriate Makefile-app from
|
||||
# within the Tock base directory.
|
||||
TOCK_BOARD ?= hail
|
||||
|
||||
# Include the makefile that has the programming functions for each board.
|
||||
include $(TOCK_USERLAND_BASE_DIR)/Program.mk
|
||||
|
||||
|
||||
|
||||
# Rules to incorporate external libraries
|
||||
define EXTERN_LIB_RULES
|
||||
EXTERN_LIB_NAME_$(notdir $(1)) := $(notdir $(1))
|
||||
|
||||
# If this library has any additional rules, add them
|
||||
-include $(1)/Makefile.app
|
||||
|
||||
# If this library has an include directory, add it to search path
|
||||
ifneq "$$(wildcard $(1)/include)" ""
|
||||
override CPPFLAGS += -I$(1)/include
|
||||
endif
|
||||
|
||||
# Add arch-specific rules for each library
|
||||
# Use the $(LIBNAME)_BUILDDIR as build directory, if set.
|
||||
$$(notdir $(1))_BUILDDIR ?= $(1)/build
|
||||
$$(foreach arch, $$(TOCK_ARCHS), $$(eval LIBS_$$(arch) += $$($(notdir $(1))_BUILDDIR)/$$(arch)/$(notdir $(1)).a))
|
||||
|
||||
endef
|
||||
|
||||
# To see the generated rules, run:
|
||||
# $(info $(foreach lib, $(EXTERN_LIBS), $(call EXTERN_LIB_RULES,$(lib))))
|
||||
$(foreach lib, $(EXTERN_LIBS), $(eval $(call EXTERN_LIB_RULES,$(lib))))
|
||||
|
||||
|
||||
# Some sanity checks for variables before they are used
|
||||
|
||||
# Warn users about LDFLAGS currently being ignored. We currently use the WLFLAGS
|
||||
# and WLFLAGS_$(arch) variables to pass linker options through the compiler, by
|
||||
# encoding them as `-Wl,...` options.
|
||||
ifdef LDFLAGS
|
||||
$(warning *******************************************************)
|
||||
$(warning LDFLAGS are currently ignored!!)
|
||||
$(warning )
|
||||
$(warning This is because we need to invoke the gcc frontend not the)
|
||||
$(warning ld frontend for the final link step, which means that we would)
|
||||
$(warning need to parse the LDFLAGS into things like -Wl,-<flag> for each)
|
||||
$(warning entry, but that proved a little fragile on first attempt so)
|
||||
$(warning it is not currently done. Sorry.)
|
||||
$(warning Please use WLFLAGS if you need to pass linker flags.)
|
||||
$(warning *******************************************************)
|
||||
endif
|
||||
|
||||
# Warn users about improperly defined `HEAP_SIZE`.
|
||||
ifdef HEAP_SIZE
|
||||
$(warning The variable HEAP_SIZE is set but will not be used.)
|
||||
$(warning Tock has two heaps, the application heap which is memory your)
|
||||
$(warning program uses and the kernel heap or grant regions, which is memory)
|
||||
$(warning dynamically allocated by drivers on behalf of your program.)
|
||||
$(warning )
|
||||
$(warning These regions are controlled by the APP_HEAP_SIZE and)
|
||||
$(warning KERNEL_HEAP_SIZE variables respectively.)
|
||||
endif
|
||||
|
||||
|
||||
# Rules to create object files for a specific architecture.
|
||||
#
|
||||
# - Argument $(1) is the architecture (e.g. cortex-m0) to compile for.
|
||||
define BUILD_RULES_PER_ARCH
|
||||
|
||||
# BUILDDIR holds architecture dependent, but board-independent outputs
|
||||
$$(BUILDDIR)/$(1):
|
||||
$$(TRACE_DIR)
|
||||
$$(Q)mkdir -p $$@
|
||||
|
||||
# First step doesn't actually compile, just generate header dependency information
|
||||
# More info on our approach here: http://stackoverflow.com/questions/97338
|
||||
$$(BUILDDIR)/$(1)/%.o: %.c | $$(BUILDDIR)/$(1)
|
||||
$$(TRACE_CC)
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(CC_$(1)) $$(CFLAGS) $$(CFLAGS_$(1)) $$(CPPFLAGS) $$(CPPFLAGS_$(1)) -MF"$$(@:.o=.d)" -MG -MM -MP -MT"$$(@:.o=.d)@" -MT"$$@" "$$<"
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(CC_$(1)) $$(CFLAGS) $$(CFLAGS_$(1)) $$(CPPFLAGS) $$(CPPFLAGS_$(1)) -c -o $$@ $$<
|
||||
|
||||
$$(BUILDDIR)/$(1)/%.o: %.cc | $$(BUILDDIR)/$(1)
|
||||
$$(TRACE_CXX)
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(CPPFLAGS_$(1)) -MF"$$(@:.o=.d)" -MG -MM -MP -MT"$$(@:.o=.d)@" -MT"$$@" "$$<"
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(CPPFLAGS_$(1)) -c -o $$@ $$<
|
||||
|
||||
$$(BUILDDIR)/$(1)/%.o: %.cpp | $$(BUILDDIR)/$(1)
|
||||
$$(TRACE_CXX)
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(CPPFLAGS_$(1)) -MF"$$(@:.o=.d)" -MG -MM -MP -MT"$$(@:.o=.d)@" -MT"$$@" "$$<"
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(CPPFLAGS_$(1)) -c -o $$@ $$<
|
||||
|
||||
$$(BUILDDIR)/$(1)/%.o: %.cxx | $$(BUILDDIR)/$(1)
|
||||
$$(TRACE_CXX)
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(CPPFLAGS_$(1)) -MF"$$(@:.o=.d)" -MG -MM -MP -MT"$$(@:.o=.d)@" -MT"$$@" "$$<"
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(CPPFLAGS_$(1)) -c -o $$@ $$<
|
||||
|
||||
OBJS_$(1) += $$(patsubst %.c,$$(BUILDDIR)/$(1)/%.o,$$(C_SRCS))
|
||||
OBJS_$(1) += $$(patsubst %.cc,$$(BUILDDIR)/$(1)/%.o,$$(filter %.cc, $$(CXX_SRCS)))
|
||||
OBJS_$(1) += $$(patsubst %.cpp,$$(BUILDDIR)/$(1)/%.o,$$(filter %.cpp, $$(CXX_SRCS)))
|
||||
OBJS_$(1) += $$(patsubst %.cxx,$$(BUILDDIR)/$(1)/%.o,$$(filter %.cxx, $$(CXX_SRCS)))
|
||||
|
||||
endef
|
||||
|
||||
|
||||
# Rules to generate an app for a given architecture and target. These actually
|
||||
# create the ELF which can be linked for a specific address as needed.
|
||||
#
|
||||
# - Argument $(1) is the architecture (e.g. cortex-m0) to build for.
|
||||
# - Argument $(2) is the output name to use for the .elf and other files.
|
||||
# - Argument $(3) is the flash address to use for linking.
|
||||
# - Argument $(4) is the RAM address to use for linking.
|
||||
#
|
||||
# Note: all variables, other than $(1), used within this block must be double
|
||||
# dollar-signed so that their values will be evaluated when run, not when
|
||||
# generated
|
||||
define BUILD_RULES
|
||||
|
||||
$$(BUILDDIR)/$(1)/$(2).custom.ld: $$(LAYOUT) | $$(BUILDDIR)/$(1)
|
||||
@# Start with a copy of the template / generic ld script
|
||||
$$(Q)cp $$< $$@
|
||||
@# #616 #635: sed is not cross-platform
|
||||
@# https://stackoverflow.com/a/22247781/358675 <-- Use perl in place of sed
|
||||
$$(Q)\
|
||||
perl -pi -e "s/(FLASH.*ORIGIN[ =]*)([x0-9]*)(,.*LENGTH)/\$$$${1}$(3)\$$$$3/" $$@ &&\
|
||||
perl -pi -e "s/(SRAM.*ORIGIN[ =]*)([x0-9]*)(,.*LENGTH)/\$$$${1}$(4)\$$$$3/" $$@
|
||||
|
||||
# Collect all desired built output.
|
||||
$$(BUILDDIR)/$(1)/$(2).elf: $$(OBJS_$(1)) $$(LIBS_$(1)) $$(LEGACY_LIBS_$(1)) $$(BUILDDIR)/$(1)/$(2).custom.ld | $$(BUILDDIR)/$(1)
|
||||
$$(TRACE_LD)
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(CC_$(1)) $$(CFLAGS) $$(CFLAGS_$(1)) $$(CPPFLAGS) $$(CPPFLAGS_$(1)) $$(WLFLAGS) $$(WLFLAGS_$(1))\
|
||||
-Xlinker --defsym=STACK_SIZE=$$(STACK_SIZE)\
|
||||
-Xlinker --defsym=APP_HEAP_SIZE=$$(APP_HEAP_SIZE)\
|
||||
-Xlinker --defsym=KERNEL_HEAP_SIZE=$$(KERNEL_HEAP_SIZE)\
|
||||
-T $$(BUILDDIR)/$(1)/$(2).custom.ld\
|
||||
-nostdlib\
|
||||
-Wl,--start-group $$(OBJS_$(1)) $$(LIBS_$(1)) $$(LEGACY_LIBS_$(1)) $$(LINK_LIBS_$(1)) -Wl,--end-group\
|
||||
-Wl,-Map=$$(BUILDDIR)/$(1)/$(2).Map\
|
||||
-o $$@
|
||||
|
||||
# NOTE: This rule creates an lst file for the elf as flashed on the board
|
||||
# (i.e. at address 0x80000000). This is not likely what you want.
|
||||
$$(BUILDDIR)/$(1)/$(2).lst: $$(BUILDDIR)/$(1)/$(2).elf
|
||||
$$(TRACE_LST)
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(OBJDUMP) $$(OBJDUMP_FLAGS) $$(OBJDUMP_FLAGS_$(1)) $$< > $$@
|
||||
@echo $$$$(tput bold)Listings generated at $$@$$$$(tput sgr0)
|
||||
|
||||
# checks compiled ELF files to ensure that all libraries and applications were
|
||||
# built with the correct flags in order to work on a Tock board
|
||||
.PHONY: validate_gcc_flags
|
||||
validate_gcc_flags:: $$(BUILDDIR)/$(1)/$(2).elf
|
||||
ifndef TOCK_NO_CHECK_SWITCHES
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(READELF) -p .GCC.command.line $$< 2>&1 | grep -q "does not exist" && { echo "Error: Missing section .GCC.command.line"; echo ""; echo "Tock requires that applications are built with"; echo " -frecord-gcc-switches"; echo "to validate that all required flags were used"; echo ""; echo "You can skip this check by defining the make variable TOCK_NO_CHECK_SWITCHES"; exit 1; } || exit 0
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(READELF) -p .GCC.command.line $$< | grep -q -- -msingle-pic-base && $$(READELF) -p .GCC.command.line $$< | grep -q -- -mpic-register=r9 && $$(READELF) -p .GCC.command.line $$< | grep -q -- -mno-pic-data-is-text-relative || { echo "Error: Missing required build flags."; echo ""; echo "Tock requires applications are built with"; echo " -msingle-pic-base"; echo " -mpic-register=r9"; echo " -mno-pic-data-is-text-relative"; echo "But one or more of these flags are missing"; echo ""; echo "To see the flags your application was built with, run"; echo "$$(READELF) -p .GCC.command.line $$<"; echo ""; exit 1; }
|
||||
endif
|
||||
|
||||
# rules to print the size of the built binaries
|
||||
.PHONY: size-$(1)-$(2)
|
||||
size-$(1)-$(2): $$(BUILDDIR)/$(1)/$(2).elf
|
||||
@echo Application size report for target $(2):
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(SIZE) $$^
|
||||
|
||||
size:: size-$(1)-$(2)
|
||||
|
||||
|
||||
############################################################################################
|
||||
# DEBUGGING STUFF
|
||||
#
|
||||
# The approach here is that we're going create a new elf file that is compiled
|
||||
# at the actual flash and ram offset of the loaded program
|
||||
#
|
||||
# We want to build a rule that fails if these needed env variables aren't set
|
||||
# only when actually trying to use them to build the lst file. We also want to
|
||||
# force this to rerun every time it's invoked so that it picks up new env
|
||||
# variable settings
|
||||
|
||||
|
||||
# Step 0: Force this to be built every time
|
||||
.PHONY: _FORCE_USERLAND_DEBUG_LD
|
||||
|
||||
# Step 1: Create a new linker script. Note this depends on original (non-shifted) elf
|
||||
# (supposedly this could be one-lined, but I couldn't make that work, so here goes)
|
||||
ifdef RAM_START
|
||||
ifdef FLASH_INIT
|
||||
_USERLAND_DEBUG_ALL_NEEDED_VARS := 1
|
||||
endif
|
||||
endif
|
||||
|
||||
$$(BUILDDIR)/$(1)/$(2).userland_debug.ld: $$(TOCK_USERLAND_BASE_DIR)/userland_generic.ld $$(BUILDDIR)/$(1)/$(2).elf _FORCE_USERLAND_DEBUG_LD | $$(BUILDDIR)/$(1)
|
||||
ifndef _USERLAND_DEBUG_ALL_NEEDED_VARS
|
||||
@echo "ERROR: Required variables RAM_START and FLASH_INIT are not set."
|
||||
@echo " These are needed to compute the offset your program was loaded at."
|
||||
@echo " See the kernel panic message for these values."
|
||||
@exit 1
|
||||
else
|
||||
@# Start with a copy of the template / generic ld script
|
||||
$$(Q)cp $$< $$@
|
||||
@# And with apologies to future readers, this is easier as one shell command/script so
|
||||
@# we can set intervening variables, away we go
|
||||
@#
|
||||
@# Get the offset between the init function and the start of text (0x80000000).
|
||||
@# We then use that offset to calculate where the start of text was on the actual MCU.
|
||||
@# Create a new LD file at the correct flash and ram locations.
|
||||
@#
|
||||
@# #616 #635: sed is not cross-platform
|
||||
@# https://stackoverflow.com/a/22247781/358675 <-- Use perl in place of sed
|
||||
$$(Q)set -e ;\
|
||||
ORIGINAL_ENTRY=`$$(TOOLCHAIN_$(1))$$(READELF) -h $$(BUILDDIR)/$(1)/$(2).elf | grep Entry | awk '{print $$$$4}'` ;\
|
||||
INIT_OFFSET=$$$$(($$$$ORIGINAL_ENTRY - 0x80000000)) ;\
|
||||
FLASH_START=$$$$(($$$$FLASH_INIT-$$$$INIT_OFFSET)) ;\
|
||||
perl -pi -e "s/(FLASH.*ORIGIN[ =]*)([x0-9]*)(,.*LENGTH)/\$$$${1}$$$$FLASH_START\$$$$3/" $$@ ;\
|
||||
perl -pi -e "s/(SRAM.*ORIGIN[ =]*)([x0-9]*)(,.*LENGTH)/\$$$${1}$$$$RAM_START\$$$$3/" $$@
|
||||
endif
|
||||
|
||||
# Step 2: Create a new ELF with the layout that matches what's loaded
|
||||
$$(BUILDDIR)/$(1)/$(2).userland_debug.elf: $$(OBJS_$(1)) $$(LIBS_$(1)) $$(LEGACY_LIBS_$(1)) $$(BUILDDIR)/$(1)/$(2).userland_debug.ld | $$(BUILDDIR)/$(1)
|
||||
$$(TRACE_LD)
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(CC_$(1)) $$(CFLAGS) $$(CFLAGS_$(1)) $$(CPPFLAGS) $$(CPPFLAGS_$(1)) $$(WLFLAGS) $$(WLFLAGS_$(1))\
|
||||
-Xlinker --defsym=STACK_SIZE=$$(STACK_SIZE)\
|
||||
-Xlinker --defsym=APP_HEAP_SIZE=$$(APP_HEAP_SIZE)\
|
||||
-Xlinker --defsym=KERNEL_HEAP_SIZE=$$(KERNEL_HEAP_SIZE)\
|
||||
-T $$(BUILDDIR)/$(1)/$(2).userland_debug.ld\
|
||||
-nostdlib\
|
||||
-Wl,--start-group $$(OBJS_$(1)) $$(LIBS_$(1)) $$(LEGACY_LIBS_$(1)) $$(LINK_LIBS_$(1)) -Wl,--end-group\
|
||||
-Wl,-Map=$$(BUILDDIR)/$(1)/$(2).Map\
|
||||
-o $$@
|
||||
|
||||
# Step 3: Now we can finally generate an LST
|
||||
$$(BUILDDIR)/$(1)/$(2).userland_debug.lst: $$(BUILDDIR)/$(1)/$(2).userland_debug.elf
|
||||
$$(TRACE_LST)
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(OBJDUMP) $$(OBJDUMP_FLAGS) $$(OBJDUMP_FLAGS_$(1)) $$< > $$@
|
||||
@echo $$$$(tput bold)Listings generated at $$@$$$$(tput sgr0)
|
||||
|
||||
# END DEBUGGING STUFF
|
||||
############################################################################################
|
||||
endef
|
||||
|
||||
|
||||
# Rules that apply to an entire architecture family (e.g. cortex-m).
|
||||
#
|
||||
# - Argument $(1) is the architecture family (e.g. cortex-m).
|
||||
# - Argument $(2) is the list of architectures in that family.
|
||||
# - Argument $(3) is the list of output names for the .elf of each arch.
|
||||
#
|
||||
# Note: all variables, other than $(1), used within this block must be double
|
||||
# dollar-signed so that their values will be evaluated when run, not when
|
||||
# generated.
|
||||
define ARCH_FAMILY_RULES
|
||||
|
||||
$(1)_DIRECTORY_NAMES := $$(addsuffix /,$(2))
|
||||
$(1)_DIRECTORY_FILES := $$(join $$($(1)_DIRECTORY_NAMES),$(3))
|
||||
$(1)_DIRECTORY_FILES_EXT := $$(addsuffix .elf,$$($(1)_DIRECTORY_FILES))
|
||||
$(1)_ELF_FILES := $$(addprefix $$(BUILDDIR)/,$$($(1)_DIRECTORY_FILES_EXT))
|
||||
|
||||
# Rule to print the size of the built binaries from an architecture family.
|
||||
.PHONY: size-$(1)
|
||||
size-$(1): $$($(1)_ELF_FILES)
|
||||
@echo Application size report for arch family $(1):
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(SIZE) -t $$^
|
||||
|
||||
endef
|
||||
|
||||
# Functions to parse the `TOCK_TARGETS` array. Entries 3 and 4 default to the
|
||||
# PIC addresses if they are not specified.
|
||||
ARCH_FN = $(firstword $(subst |, ,$1))
|
||||
OUTPUT_NAME_FN = $(if $(word 2,$(subst |, ,$1)),$(word 2,$(subst |, ,$1)),$(firstword $(subst |, ,$1)))
|
||||
FLASH_ADDRESS_FN = $(if $(word 3,$(subst |, ,$1)),$(word 3,$(subst |, ,$1)),0x80000000)
|
||||
RAM_ADDRESS_FN = $(if $(word 4,$(subst |, ,$1)),$(word 4,$(subst |, ,$1)),0x00000000)
|
||||
|
||||
# To see the generated rules, run:
|
||||
#$(info $(foreach platform, $(TOCK_ARCHS), $(eval $(call BUILD_RULES_PER_ARCH,$(platform)))))
|
||||
#$(info $(foreach platform, $(TOCK_TARGETS), $(call BUILD_RULES,$(call ARCH_FN,$(platform)),$(call OUTPUT_NAME_FN,$(platform)),$(call FLASH_ADDRESS_FN,$(platform)),$(call RAM_ADDRESS_FN,$(platform)))))
|
||||
# Actually generate the rules for each architecture
|
||||
$(foreach platform, $(TOCK_ARCHS), $(eval $(call BUILD_RULES_PER_ARCH,$(platform))))
|
||||
$(foreach platform, $(TOCK_TARGETS), $(eval $(call BUILD_RULES,$(call ARCH_FN,$(platform)),$(call OUTPUT_NAME_FN,$(platform)),$(call FLASH_ADDRESS_FN,$(platform)),$(call RAM_ADDRESS_FN,$(platform)))))
|
||||
$(foreach family, $(TOCK_ARCH_FAMILIES), $(eval $(call ARCH_FAMILY_RULES,$(family),$(foreach target, $(filter $(family)%,$(TOCK_TARGETS)), $(call ARCH_FN, $(target))),$(foreach target, $(filter $(family)%,$(TOCK_TARGETS)), $(call OUTPUT_NAME_FN, $(target))))))
|
||||
|
||||
|
||||
|
||||
|
||||
# TAB file generation. Used for Tockloader
|
||||
$(BUILDDIR)/$(PACKAGE_NAME).tab: $(foreach platform, $(TOCK_TARGETS), $(BUILDDIR)/$(call ARCH_FN,$(platform))/$(call OUTPUT_NAME_FN,$(platform)).elf)
|
||||
$(TRACE_E2T)
|
||||
$(Q)$(ELF2TAB) $(ELF2TAB_ARGS) -o $@ $^
|
||||
|
||||
|
||||
|
||||
# Rules for building apps
|
||||
SIZE_RULES = $(addprefix size-,$(TOCK_ARCH_FAMILIES))
|
||||
.PHONY: all
|
||||
all: $(BUILDDIR)/$(PACKAGE_NAME).tab $(SIZE_RULES)
|
||||
|
||||
# The size target accumulates dependencies in the platform build rule creation
|
||||
.PHONY: size
|
||||
|
||||
# Generate helpful output for debugging userland applications.
|
||||
.PHONY: debug
|
||||
debug: $(foreach platform, $(TOCK_TARGETS), $(BUILDDIR)/$(call ARCH_FN,$(platform))/$(call OUTPUT_NAME_FN,$(platform)).userland_debug.lst)
|
||||
|
||||
# Generate a .lst file for each architecture using the RAM and flash addresses
|
||||
# specified in the linker file. This will create a valid assembly file, but the
|
||||
# addresses of the instructions will be wrong unless the application was
|
||||
# compiled for specific addresses. Notably, on cortex-m platforms, which use
|
||||
# position-independent code, the addresses will be wrong, and you should use
|
||||
# `make debug` instead. For architectures without PIC support (like RISC-V),
|
||||
# `make lst` will work since the linker files uses the correct addresses.
|
||||
.PHONY: lst
|
||||
lst: $(foreach platform, $(TOCK_TARGETS), $(BUILDDIR)/$(call ARCH_FN,$(platform))/$(call OUTPUT_NAME_FN,$(platform)).lst)
|
||||
|
||||
.PHONY:
|
||||
clean::
|
||||
rm -Rf $(BUILDDIR)
|
||||
|
||||
|
||||
# Rules for running the C linter
|
||||
FORMATTED_FILES := $(patsubst %.c,$(BUILDDIR)/format/%.uncrustify,$(C_SRCS))
|
||||
FORMATTED_FILES += $(patsubst %.cc,$(BUILDDIR)/format/%.uncrustify,$(filter %.cc, $(CXX_SRCS)))
|
||||
FORMATTED_FILES += $(patsubst %.cpp,$(BUILDDIR)/format/%.uncrustify,$(filter %.cpp, $(CXX_SRCS)))
|
||||
FORMATTED_FILES += $(patsubst %.cxx,$(BUILDDIR)/format/%.uncrustify,$(filter %.cxx, $(CXX_SRCS)))
|
||||
|
||||
$(BUILDDIR)/format:
|
||||
@mkdir -p $@
|
||||
|
||||
.PHONY: fmt format
|
||||
fmt format:: $(FORMATTED_FILES)
|
||||
|
||||
$(BUILDDIR)/format/%.uncrustify: %.c | _format_check_unstaged
|
||||
$(Q)$(UNCRUSTIFY) -f $< -o $@
|
||||
$(Q)cmp -s $< $@ || (if [ "$$CI" = "true" ]; then diff -y $< $@; rm $@; exit 1; else cp $@ $<; fi)
|
||||
$(BUILDDIR)/format/%.uncrustify: %.cc | _format_check_unstaged
|
||||
$(Q)$(UNCRUSTIFY) -f $< -o $@
|
||||
$(Q)cmp -s $< $@ || (if [ "$$CI" = "true" ]; then diff -y $< $@; rm $@; exit 1; else cp $@ $<; fi)
|
||||
$(BUILDDIR)/format/%.uncrustify: %.cpp | _format_check_unstaged
|
||||
$(Q)$(UNCRUSTIFY) -f $< -o $@
|
||||
$(Q)cmp -s $< $@ || (if [ "$$CI" = "true" ]; then diff -y $< $@; rm $@; exit 1; else cp $@ $<; fi)
|
||||
$(BUILDDIR)/format/%.uncrustify: %.cxx | _format_check_unstaged
|
||||
$(Q)$(UNCRUSTIFY) -f $< -o $@
|
||||
$(Q)cmp -s $< $@ || (if [ "$$CI" = "true" ]; then diff -y $< $@; rm $@; exit 1; else cp $@ $<; fi)
|
||||
|
||||
|
||||
# Rules to help validate build configuration
|
||||
fmt format::
|
||||
$(Q)$(TOCK_USERLAND_BASE_DIR)/tools/check_override.sh
|
||||
|
||||
|
||||
#########################################################################################
|
||||
# Include dependency rules for picking up header changes (by convention at bottom of makefile)
|
||||
OBJS_NO_ARCHIVES += $(filter %.o,$(foreach platform, $(TOCK_ARCHS), $(OBJS_$(platform))))
|
||||
-include $(OBJS_NO_ARCHIVES:.o=.d)
|
@ -0,0 +1,578 @@
|
||||
################################################################################
|
||||
##
|
||||
## libtock-c build system configuration.
|
||||
##
|
||||
## This sets all of the parameters and flags required to build libtock-c
|
||||
## applications for the architectures Tock supports.
|
||||
##
|
||||
## Included by AppMakefile.mk and TockLibrary.mk.
|
||||
##
|
||||
################################################################################
|
||||
|
||||
# Ensure that this file is only included once.
|
||||
ifndef CONFIGURATION_MAKEFILE
|
||||
CONFIGURATION_MAKEFILE = 1
|
||||
|
||||
# Remove built-in rules and variables
|
||||
# n.b. no-op for make --version < 4.0
|
||||
MAKEFLAGS += -r
|
||||
MAKEFLAGS += -R
|
||||
|
||||
# Toolchain programs.
|
||||
AR := -ar
|
||||
AS := -as
|
||||
CXX := -g++
|
||||
OBJDUMP := -objdump
|
||||
RANLIB := -ranlib
|
||||
READELF := -readelf
|
||||
SIZE := -size
|
||||
|
||||
# Set default region sizes for process memory requirements.
|
||||
STACK_SIZE ?= 2048
|
||||
APP_HEAP_SIZE ?= 1024
|
||||
KERNEL_HEAP_SIZE ?= 1024
|
||||
|
||||
# Set default required kernel version.
|
||||
KERNEL_MAJOR_VERSION ?= 2
|
||||
KERNEL_MINOR_VERSION ?= 0
|
||||
|
||||
# PACKAGE_NAME is used to identify the application for IPC and for error
|
||||
# reporting. This can be overwritten per-app to customize the name, otherwise we
|
||||
# default to the name of the directory the app is in.
|
||||
PACKAGE_NAME ?= $(shell basename "$(shell pwd)")
|
||||
|
||||
# Tock app targets.
|
||||
#
|
||||
# This is a list of all of the different targets to build an app for which will
|
||||
# all be bundled into a TAB. This allows us to build an app for any board Tock
|
||||
# supports, and wait until a TAB is installed onto a board to figure out which
|
||||
# specific binary that hardware platform needs.
|
||||
#
|
||||
# Each entry is itself a list:
|
||||
#
|
||||
# 1. The name of the architecture. This is used for naming generated files and
|
||||
# variables in the Makefiles. It is generally just a human-readable name.
|
||||
# 2. (Optional) The name to use when creating the output file.
|
||||
# 3. (Optional) The address to use as the fixed start of flash.
|
||||
# 4. (Optional) The address to use as the fixed start of RAM.
|
||||
#
|
||||
# By default we currently only build the Cortex-M targets. To enable the RISC-V
|
||||
# targets, set the RISCV variable like so:
|
||||
#
|
||||
# $ make RISCV=1
|
||||
#
|
||||
# Once the RV32 toolchain distribution stabilizes (as of June 2020 the toolchain
|
||||
# isn't as easily obtained as we would like), we intend to make the RISC-V
|
||||
# targets build by default as well.
|
||||
ifeq ($(RISCV),)
|
||||
TOCK_TARGETS ?= cortex-m0 cortex-m3 cortex-m4 cortex-m7
|
||||
else
|
||||
# Specific addresses useful for the OpenTitan hardware memory map.
|
||||
OPENTITAN_TOCK_TARGETS := rv32imc|rv32imc.0x20030080.0x10005000|0x20030080|0x10005000\
|
||||
rv32imc|rv32imc.0x20030880.0x10008000|0x20030880|0x10008000\
|
||||
rv32imc|rv32imc.0x20032080.0x10008000|0x20032080|0x10008000\
|
||||
rv32imc|rv32imc.0x20034080.0x10008000|0x20034080|0x10008000
|
||||
|
||||
# Specific addresses useful for the ARTY-E21 FPGA softcore hardware memory map.
|
||||
ARTY_E21_TOCK_TARGETS := rv32imac|rv32imac.0x40430060.0x80004000|0x40430060|0x80004000\
|
||||
rv32imac|rv32imac.0x40440060.0x80007000|0x40440060|0x80007000
|
||||
|
||||
# Include the RISC-V targets.
|
||||
# rv32imac|rv32imac.0x20040060.0x80002800 # RISC-V for HiFive1b
|
||||
# rv32imac|rv32imac.0x403B0060.0x3FCC0000 # RISC-V for ESP32-C3
|
||||
# rv32imc|rv32imc.0x41000060.0x42008000 # RISC-V for LiteX Arty-A7
|
||||
# rv32imc|rv32imc.0x00080060.0x40008000 # RISC-V for LiteX Simulator
|
||||
TOCK_TARGETS ?= cortex-m0\
|
||||
cortex-m3\
|
||||
cortex-m4\
|
||||
cortex-m7\
|
||||
rv32imac|rv32imac.0x20040060.0x80002800|0x20040060|0x80002800\
|
||||
rv32imac|rv32imac.0x403B0060.0x3FCC0000|0x403B0060|0x3FCC0000\
|
||||
rv32imc|rv32imc.0x41000060.0x42008000|0x41000060|0x42008000\
|
||||
rv32imc|rv32imc.0x00080060.0x40008000|0x00080060|0x40008000\
|
||||
$(OPENTITAN_TOCK_TARGETS) \
|
||||
$(ARTY_E21_TOCK_TARGETS)
|
||||
endif
|
||||
|
||||
# Generate `TOCK_ARCH_FAMILIES`, the set of architecture families which will be
|
||||
# used to determine toolchains to use in the build process.
|
||||
TOCK_ARCH_FAMILIES := $(sort $(foreach target, $(TOCK_TARGETS), $(strip \
|
||||
$(findstring rv32i,$(target)) \
|
||||
$(findstring cortex-m,$(target)))))
|
||||
|
||||
# Generate `TOCK_ARCHS`, the set of architectures listed in `TOCK_TARGETS`.
|
||||
#
|
||||
# The architecture name is used extensively to create the correct build commands
|
||||
# for each architecture. Make targets are automatically generated in
|
||||
# `AppMakefile.mk` based on the list of `TOCK_TARGETS`. The remainder of this
|
||||
# file uses the architecture name to pull the correct flags for each stage in
|
||||
# the build process.
|
||||
TOCK_ARCHS := $(sort $(foreach target, $(TOCK_TARGETS), $(firstword $(subst |, ,$(target)))))
|
||||
|
||||
# Check if elf2tab exists, if not, install it using cargo.
|
||||
ELF2TAB ?= elf2tab
|
||||
ELF2TAB_REQUIRED_VERSION := 0.7.0
|
||||
ELF2TAB_EXISTS := $(shell $(SHELL) -c "command -v $(ELF2TAB)")
|
||||
ELF2TAB_VERSION := $(shell $(SHELL) -c "$(ELF2TAB) --version | cut -d ' ' -f 2")
|
||||
|
||||
# Check elf2tab version.
|
||||
UPGRADE_ELF2TAB := $(shell $(SHELL) -c "printf '%s\n%s\n' '$(ELF2TAB_REQUIRED_VERSION)' '$(ELF2TAB_VERSION)' | sort --check=quiet --version-sort || echo yes")
|
||||
|
||||
ifeq ($(UPGRADE_ELF2TAB),yes)
|
||||
$(info Trying to update elf2tab to >= $(ELF2TAB_REQUIRED_VERSION))
|
||||
ELF2TAB_EXISTS =
|
||||
endif
|
||||
|
||||
ifndef ELF2TAB_EXISTS
|
||||
$(shell cargo install elf2tab)
|
||||
# Check elf2tab version after install
|
||||
ELF2TAB_VERSION := $(shell $(SHELL) -c "$(ELF2TAB) --version | cut -d ' ' -f 2")
|
||||
UPGRADE_ELF2TAB := $(shell $(SHELL) -c "printf '%s\n%s\n' '$(ELF2TAB_REQUIRED_VERSION)' '$(ELF2TAB_VERSION)' | sort --check=quiet --version-sort || echo yes")
|
||||
ifeq ($(UPGRADE_ELF2TAB),yes)
|
||||
$(error Failed to automatically update elf2tab, please update manually elf2tab to >= $(ELF2TAB_REQUIRED_VERSION))
|
||||
endif
|
||||
endif
|
||||
|
||||
################################################################################
|
||||
##
|
||||
## Shared build flags for all architectures in libtock-c.
|
||||
##
|
||||
################################################################################
|
||||
|
||||
# elf2tab flags.
|
||||
#
|
||||
# Provide the name, memory sizes, and required kernel version as arguments to
|
||||
# elf2tab so it can include the parameters in the TBF header.
|
||||
ELF2TAB_ARGS += -n $(PACKAGE_NAME)
|
||||
ELF2TAB_ARGS += --stack $(STACK_SIZE) --app-heap $(APP_HEAP_SIZE) --kernel-heap $(KERNEL_HEAP_SIZE)
|
||||
ELF2TAB_ARGS += --kernel-major $(KERNEL_MAJOR_VERSION) --kernel-minor $(KERNEL_MINOR_VERSION)
|
||||
|
||||
# Flags for building app Assembly, C, and C++ files used by all architectures.
|
||||
# n.b. CPPFLAGS are shared for C and C++ sources (it's short for C PreProcessor,
|
||||
# and C++ uses the C preprocessor). To specify flags for only C or C++, use
|
||||
# CFLAGS for C only and CXXFLAGS for C++ only. [While we're on the trivia
|
||||
# lesson, CXX is shorthand for C++ because folks on the unix/gnu side of history
|
||||
# needed a valid letter rather than a symbol (an X is a rotated +). Confusingly,
|
||||
# the dos/microsoft lineage chose `.cpp` to address this same issue, leading to
|
||||
# confusion nowadays about the meaning of 'cpp'.]
|
||||
override ASFLAGS += -mthumb
|
||||
override CFLAGS += -std=gnu11
|
||||
override CPPFLAGS += \
|
||||
-frecord-gcc-switches\
|
||||
-gdwarf-2\
|
||||
-Os\
|
||||
-fdata-sections -ffunction-sections\
|
||||
-fstack-usage\
|
||||
-D_FORTIFY_SOURCE=2\
|
||||
-Wall\
|
||||
-Wextra
|
||||
override WLFLAGS += \
|
||||
-Wl,--warn-common\
|
||||
-Wl,--gc-sections\
|
||||
-Wl,--build-id=none
|
||||
|
||||
# Flags to improve the quality and information in listings (debug target)
|
||||
OBJDUMP_FLAGS += --disassemble-all --source -C --section-headers
|
||||
|
||||
# Use a generic linker script for all libtock-c apps.
|
||||
LAYOUT ?= $(TOCK_USERLAND_BASE_DIR)/userland_generic.ld
|
||||
|
||||
# Various flags for a specific toolchain. Different compilers may have different
|
||||
# supported features. For GCC we warn if the compiler estimates the stack usage
|
||||
# will be greater than the allocated stack size.
|
||||
override CPPFLAGS_gcc += -Wstack-usage=$(STACK_SIZE)
|
||||
|
||||
# Generic PIC flags for architectures with compiler support for FDPIC. Note!
|
||||
# These flags are not sufficient for full PIC support as Tock requires. The
|
||||
# `-fPIC` flag generally only allows the .text and .data sections to be at
|
||||
# different relative addresses. However, the .text and RAM sections are not
|
||||
# fully relocatable. Therefore, just including these flags is not sufficient to
|
||||
# build a full PIC app for Tock. So, we split these out, and only include them
|
||||
# for architectures where we have full PIC support.
|
||||
override CPPFLAGS_PIC += \
|
||||
-Wl,--emit-relocs\
|
||||
-fPIC
|
||||
|
||||
################################################################################
|
||||
##
|
||||
## RISC-V compiler/linker flags
|
||||
##
|
||||
################################################################################
|
||||
|
||||
# RISC-V toolchains, irrespective of their name-tuple, can compile for
|
||||
# essentially any target. Thus, try a few known names and choose the one for
|
||||
# which a compiler is found.
|
||||
ifneq (,$(shell which riscv64-none-elf-gcc 2>/dev/null))
|
||||
TOOLCHAIN_rv32i := riscv64-none-elf
|
||||
else ifneq (,$(shell which riscv32-none-elf-gcc 2>/dev/null))
|
||||
TOOLCHAIN_rv32i := riscv32-none-elf
|
||||
else ifneq (,$(shell which riscv64-elf-gcc 2>/dev/null))
|
||||
TOOLCHAIN_rv32i := riscv64-elf
|
||||
else ifneq (,$(shell which riscv64-unknown-elf-clang 2>/dev/null))
|
||||
TOOLCHAIN_rv32i := riscv64-unknown-elf
|
||||
else ifneq (,$(shell which riscv32-unknown-elf-clang 2>/dev/null))
|
||||
TOOLCHAIN_rv32i := riscv32-unknown-elf
|
||||
else
|
||||
# Fallback option. We don't particularly want to throw an error (even if
|
||||
# RISCV=1 is set) as this configuration makefile can be useful without a
|
||||
# proper toolchain.
|
||||
TOOLCHAIN_rv32i := riscv64-unknown-elf
|
||||
endif
|
||||
TOOLCHAIN_rv32imac := $(TOOLCHAIN_rv32i)
|
||||
TOOLCHAIN_rv32imc := $(TOOLCHAIN_rv32i)
|
||||
|
||||
# For RISC-V we default to GCC, but can support clang as well. Eventually, one
|
||||
# or both toolchains might support the PIC we need, at which point we would
|
||||
# default to that.
|
||||
ifeq ($(CLANG),)
|
||||
# Default to GCC
|
||||
CC_rv32 := -gcc
|
||||
else
|
||||
# If `CLANG=1` on command line, use -clang.
|
||||
CC_rv32 := -clang
|
||||
endif
|
||||
CC_rv32i := $(CC_rv32)
|
||||
CC_rv32imc := $(CC_rv32)
|
||||
CC_rv32imac := $(CC_rv32)
|
||||
|
||||
# Set the toolchain specific flags.
|
||||
#
|
||||
# Note: There are no non-gcc, clang-specific flags currently in use, so there is
|
||||
# no equivalent CPPFLAGS_clang currently. If there are clang-only flags in the
|
||||
# future, one can/should be added.
|
||||
ifeq ($(findstring -gcc,$(CC_rv32)),-gcc)
|
||||
override CPPFLAGS_toolchain_rv32 += $(CPPFLAGS_gcc)
|
||||
override CFLAGS_toolchain_rv32 += $(CFLAGS_gcc)
|
||||
endif
|
||||
|
||||
# Set the toolchain specific `CFLAGS` for RISC-V. We use the same generic
|
||||
# toolchain flags for each RISC-V variant.
|
||||
override CFLAGS_rv32 += \
|
||||
$(CFLAGS_toolchain_rv32)
|
||||
|
||||
override CFLAGS_rv32i += $(CFLAGS_rv32)
|
||||
override CFLAGS_rv32imc += $(CFLAGS_rv32)
|
||||
override CFLAGS_rv32imac += $(CFLAGS_rv32)
|
||||
|
||||
# Set the base `CPPFLAGS` for all RISC-V variants based on the toolchain family.
|
||||
override CPPFLAGS_rv32 += \
|
||||
$(CPPFLAGS_toolchain_rv32)
|
||||
|
||||
# Set the `CPPFLAGS` for RISC-V. Here we need different flags for different
|
||||
# variants.
|
||||
override CPPFLAGS_rv32i += $(CPPFLAGS_rv32) \
|
||||
-march=rv32i\
|
||||
-mabi=ilp32\
|
||||
-mcmodel=medlow
|
||||
|
||||
override CPPFLAGS_rv32imc += $(CPPFLAGS_rv32) \
|
||||
-march=rv32imc\
|
||||
-mabi=ilp32\
|
||||
-mcmodel=medlow
|
||||
|
||||
override CPPFLAGS_rv32imac += $(CPPFLAGS_rv32) \
|
||||
-march=rv32imac\
|
||||
-mabi=ilp32\
|
||||
-mcmodel=medlow
|
||||
|
||||
# Set the base `WLFLAGS` linker flags for all RISC-V variants.
|
||||
override WLFLAGS_rv32 += \
|
||||
-Wl,--no-relax # Prevent use of global_pointer for RISC-V.
|
||||
|
||||
# Use the base linker flags for each RISC-V variant.
|
||||
override WLFLAGS_rv32i += $(WLFLAGS_rv32)
|
||||
override WLFLAGS_rv32imc += $(WLFLAGS_rv32)
|
||||
override WLFLAGS_rv32imac += $(WLFLAGS_rv32)
|
||||
|
||||
# Set the system libraries we link against for RISC-V. We support C++ apps by
|
||||
# default.
|
||||
override LINK_LIBS_rv32 += \
|
||||
-lgcc -lstdc++ -lsupc++
|
||||
|
||||
override LINK_LIBS_rv32i += $(LINK_LIBS_rv32)
|
||||
override LINK_LIBS_rv32imc += $(LINK_LIBS_rv32)
|
||||
override LINK_LIBS_rv32imac += $(LINK_LIBS_rv32)
|
||||
|
||||
# Use precompiled libaries we provide to link against.
|
||||
override LEGACY_LIBS_rv32i += \
|
||||
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32i/libc.a\
|
||||
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32i/libm.a
|
||||
|
||||
override LEGACY_LIBS_rv32im += \
|
||||
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32im/libc.a\
|
||||
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32im/libm.a
|
||||
|
||||
override LEGACY_LIBS_rv32imc += $(LEGACY_LIBS_rv32im)
|
||||
|
||||
override LEGACY_LIBS_rv32imac += \
|
||||
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32imac/libc.a\
|
||||
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32imac/libm.a
|
||||
|
||||
|
||||
################################################################################
|
||||
##
|
||||
## Cortex-M compiler/linker flags
|
||||
##
|
||||
################################################################################
|
||||
|
||||
# Setup the correct toolchain for each architecture. ARM has a standard
|
||||
# toolchain we can use for every variant.
|
||||
TOOLCHAIN_cortex-m := arm-none-eabi
|
||||
TOOLCHAIN_cortex-m0 := $(TOOLCHAIN_cortex-m)
|
||||
TOOLCHAIN_cortex-m3 := $(TOOLCHAIN_cortex-m)
|
||||
TOOLCHAIN_cortex-m4 := $(TOOLCHAIN_cortex-m)
|
||||
TOOLCHAIN_cortex-m7 := $(TOOLCHAIN_cortex-m)
|
||||
|
||||
# Setup the correct compiler. For cortex-m we only support GCC as it is the only
|
||||
# toolchain with the PIC support we need for Tock userspace apps.
|
||||
CC_cortex-m := -gcc
|
||||
CC_cortex-m0 := $(CC_cortex-m)
|
||||
CC_cortex-m3 := $(CC_cortex-m)
|
||||
CC_cortex-m4 := $(CC_cortex-m)
|
||||
CC_cortex-m7 := $(CC_cortex-m)
|
||||
|
||||
# Based on the toolchain used by each architecture, add in toolchain-specific
|
||||
# flags. We assume that each architecture family uses the same toolchain.
|
||||
ifeq ($(findstring -gcc,$(CC_cortex-m)),-gcc)
|
||||
override CPPFLAGS_toolchain_cortex-m += $(CPPFLAGS_gcc)
|
||||
override CFLAGS_toolchain_cortex-m += $(CFLAGS_gcc)
|
||||
endif
|
||||
|
||||
override CFLAGS_cortex-m += \
|
||||
$(CFLAGS_toolchain_cortex-m)
|
||||
|
||||
override CFLAGS_cortex-m0 += $(CFLAGS_cortex-m)
|
||||
override CFLAGS_cortex-m3 += $(CFLAGS_cortex-m)
|
||||
override CFLAGS_cortex-m4 += $(CFLAGS_cortex-m)
|
||||
override CFLAGS_cortex-m7 += $(CFLAGS_cortex-m)
|
||||
|
||||
override CPPFLAGS_cortex-m += \
|
||||
$(CPPFLAGS_toolchain_cortex-m)\
|
||||
$(CPPFLAGS_PIC)\
|
||||
-mthumb\
|
||||
-mfloat-abi=soft\
|
||||
-msingle-pic-base\
|
||||
-mpic-register=r9\
|
||||
-mno-pic-data-is-text-relative
|
||||
|
||||
# Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85606
|
||||
override CPPFLAGS_cortex-m0 += $(CPPFLAGS_cortex-m) \
|
||||
-mcpu=cortex-m0\
|
||||
-march=armv6s-m
|
||||
|
||||
override CPPFLAGS_cortex-m3 += $(CPPFLAGS_cortex-m) \
|
||||
-mcpu=cortex-m3
|
||||
|
||||
override CPPFLAGS_cortex-m4 += $(CPPFLAGS_cortex-m) \
|
||||
-mcpu=cortex-m4
|
||||
|
||||
override CPPFLAGS_cortex-m7 += $(CPPFLAGS_cortex-m) \
|
||||
-mcpu=cortex-m7
|
||||
|
||||
# Single-arch libraries, to be phased out
|
||||
override LEGACY_LIBS_cortex-m += \
|
||||
$(TOCK_USERLAND_BASE_DIR)/libc++/cortex-m/libstdc++.a\
|
||||
$(TOCK_USERLAND_BASE_DIR)/libc++/cortex-m/libsupc++.a\
|
||||
$(TOCK_USERLAND_BASE_DIR)/libc++/cortex-m/libgcc.a
|
||||
|
||||
override LEGACY_LIBS_cortex-m0 += $(LEGACY_LIBS_cortex-m) \
|
||||
$(TOCK_USERLAND_BASE_DIR)/newlib/cortex-m/v6-m/libc.a\
|
||||
$(TOCK_USERLAND_BASE_DIR)/newlib/cortex-m/v6-m/libm.a
|
||||
|
||||
override LEGACY_LIBS_cortex-m3 += $(LEGACY_LIBS_cortex-m) \
|
||||
$(TOCK_USERLAND_BASE_DIR)/newlib/cortex-m/v7-m/libc.a\
|
||||
$(TOCK_USERLAND_BASE_DIR)/newlib/cortex-m/v7-m/libm.a
|
||||
|
||||
override LEGACY_LIBS_cortex-m4 += $(LEGACY_LIBS_cortex-m) \
|
||||
$(TOCK_USERLAND_BASE_DIR)/newlib/cortex-m/v7-m/libc.a\
|
||||
$(TOCK_USERLAND_BASE_DIR)/newlib/cortex-m/v7-m/libm.a
|
||||
|
||||
override LEGACY_LIBS_cortex-m7 += $(LEGACY_LIBS_cortex-m) \
|
||||
$(TOCK_USERLAND_BASE_DIR)/newlib/cortex-m/v7-m/libc.a\
|
||||
$(TOCK_USERLAND_BASE_DIR)/newlib/cortex-m/v7-m/libm.a
|
||||
|
||||
# Cortex-M needs an additional OBJDUMP flag.
|
||||
override OBJDUMP_FLAGS_cortex-m += --disassembler-options=force-thumb
|
||||
override OBJDUMP_FLAGS_cortex-m7 += $(OBJDUMP_FLAGS_cortex-m)
|
||||
override OBJDUMP_FLAGS_cortex-m4 += $(OBJDUMP_FLAGS_cortex-m)
|
||||
override OBJDUMP_FLAGS_cortex-m3 += $(OBJDUMP_FLAGS_cortex-m)
|
||||
override OBJDUMP_FLAGS_cortex-m0 += $(OBJDUMP_FLAGS_cortex-m)
|
||||
|
||||
|
||||
################################################################################
|
||||
# Extra warning flags not enabled by Wall or Wextra.
|
||||
#
|
||||
# I read through the gcc manual and grabbed the ones that I thought might be
|
||||
# interesting / useful. Then I grabbed that snippet below to find other things
|
||||
# that were left out of the manual that may be worth adding. Below are all
|
||||
# warnings and a short description supported by (arm-none-eabi)-gcc as of
|
||||
# v6.2.1.
|
||||
#
|
||||
# http://stackoverflow.com/questions/11714827/
|
||||
# List all supported warnings and their status:
|
||||
# gcc -Wall -Wextra -Q --help=warning
|
||||
# Below are all warnings produced in an un-merged set of sorted lists
|
||||
# broken into C/C++, C only, C++ only, other languages
|
||||
#
|
||||
# TODO(Pat) libnrfserialization noise with these, but I think they're useful
|
||||
# and I want them back when I get a chance to clean that up.
|
||||
#CPPFLAGS += -Wcast-qual # # const char* -> char*
|
||||
#CPPFLAGS += -Wswitch-default # # switch w/out default (doesn't cover all cases) (maybe annoying?)
|
||||
#CFLAGS += -Wstrict-prototypes # # function defined w/out specifying argument types
|
||||
|
||||
override CPPFLAGS += -Wdate-time # # warn if __TIME__, __DATE__, or __TIMESTAMP__ used
|
||||
# ^on b/c flashing assumes same code => no flash, these enforce
|
||||
override CPPFLAGS += -Wfloat-equal # # floats used with '=' operator, likely imprecise
|
||||
override CPPFLAGS += -Wformat-nonliteral # # can't check format string (maybe disable if annoying)
|
||||
override CPPFLAGS += -Wformat-security # # using untrusted format strings (maybe disable)
|
||||
override CPPFLAGS += -Wformat-y2k # # use of strftime that assumes two digit years
|
||||
override CPPFLAGS += -Winit-self # # { int i = i }
|
||||
override CPPFLAGS += -Wmissing-declarations # # ^same? not sure how these differ
|
||||
override CPPFLAGS += -Wmissing-field-initializers # if init'ing struct w/out field names, warn if not all used
|
||||
override CPPFLAGS += -Wmissing-format-attribute # # something looks printf-like but isn't marked as such
|
||||
override CPPFLAGS += -Wmissing-noreturn # # __attribute__((noreturn)) like -> ! in Rust, should use it
|
||||
override CPPFLAGS += -Wmultichar # # use of 'foo' instead of "foo" (surpised not on by default?)
|
||||
override CPPFLAGS += -Wpointer-arith # # sizeof things not define'd (i.e. sizeof(void))
|
||||
override CPPFLAGS += -Wredundant-decls # # { int i; int i; } (a lint)
|
||||
override CPPFLAGS += -Wshadow # # int foo(int a) { int a = 1; } inner a shadows outer a
|
||||
override CPPFLAGS += -Wunused-macros # # macro defined in this file not used
|
||||
override CPPFLAGS += -Wunused-parameter # # function parameter is unused aside from its declaration
|
||||
override CPPFLAGS += -Wwrite-strings # # { char* c = "foo"; c[0] = 'b' } <-- "foo" should be r/o
|
||||
|
||||
override CPPFLAGS_gcc += -Wlogical-op # # "suspicious use of logical operators in expressions" (a lint)
|
||||
override CPPFLAGS_gcc += -Wtrampolines # # attempt to generate a trampoline on the NX stack
|
||||
|
||||
#CPPFLAGS += -Wabi -Wabi-tag # inter-compiler abi issues
|
||||
#CPPFLAGS += -Waggregate-return # warn if things return struct's
|
||||
#CPPFLAGS += -Wcast-align # { char *c; int *i = (int*) c}, 1 byte -> 4 byte align
|
||||
#CPPFLAGS += -Wconversion # implicit conversion that may unexpectedly alter value
|
||||
# ^ A ton of these from syscalls I think, XXX look later
|
||||
#CPPFLAGS += -Wdisabled-optimization # gcc skipped an optimization for any of a thousand reasons
|
||||
#CPPFLAGS += -Wdouble-promotion # warn if float -> double implicitly XXX maybe?
|
||||
#CPPFLAGS += -Wformat-signedness # # { int i; printf("%d %u", i, i) } second bad (maybe annoying?)
|
||||
# ^ Too obnoxious when you want hex of an int
|
||||
#CPPFLAGS += -Wfloat-conversion # subset of -Wconversion
|
||||
#CPPFLAGS += -Winline # something marked `inline` wasn't inlined
|
||||
#CPPFLAGS += -Winvalid-pch # bad precompiled header found in an include dir
|
||||
#CPPFLAGS += -Wmissing-include-dirs -- XXX Didn't try, afriad could be annoying
|
||||
#CPPFLAGS += -Woverlength-strings # complier compat: strings > [509 C90, 4095 C99] chars
|
||||
#CPPFLAGS += -Wpacked # struct with __attribute__((packed)) that does nothing
|
||||
#CPPFLAGS += -Wpadded # padding added to a struct. Noisy for argument structs
|
||||
#CPPFLAGS += -Wpedantic # strict ISO C/C++
|
||||
#CPPFLAGS += -Wsign-conversion # implicit integer sign conversions, part of -Wconversion
|
||||
#CPPFLAGS += -Wstack-protector # only if -fstack-protector, on by default, warn fn not protect
|
||||
#CPPFLAGS += -Wsuggest-attribute=const # does what it sounds like - removed due to noise
|
||||
#CPPFLAGS += -Wsuggest-attribute=pure # does what it sounds like - removed due to noise
|
||||
#CPPFLAGS += -Wswitch-enum # # switch of enum doesn't explicitly cover all cases
|
||||
# ^ annoying in practice, let default: do its job
|
||||
#CPPFLAGS += -Wsystem-headers # warnings from system headers
|
||||
#CPPFLAGS += -Wtraditional # stuff gcc allows that "traditional" C doesn't
|
||||
#CPPFLAGS += -Wundef # undefined identifier is evaluated in an `#if' directive
|
||||
# ^ Lots of library #if SAMD || SMAR21 stuff
|
||||
# Should probably be ifdef, but too much noise
|
||||
#CPPFLAGS += -Wunsafe-loop-optimizations # compiler can't divine loop bounds XXX maybe interesting?
|
||||
#CPPFLAGS += -Wvariadic-macros # can't be used in ISO C
|
||||
#CPPFLAGS += -Wvector-operation-performance # perf option not appropriate for these systems
|
||||
#CPPFLAGS += -Wvla -- XXX Didn't try, but interested
|
||||
|
||||
# C-only warnings
|
||||
override CFLAGS += -Wbad-function-cast # # not obvious when this would trigger, could drop if annoying
|
||||
override CFLAGS += -Wmissing-prototypes # # global fn defined w/out prototype (should be static or in .h)
|
||||
override CFLAGS += -Wnested-externs # # mis/weird-use of extern keyword
|
||||
override CFLAGS += -Wold-style-definition # # this garbage: void bar (a) int a; { }
|
||||
|
||||
override CFLAGS_gcc += -Wjump-misses-init # # goto or switch skips over a variable initialization
|
||||
|
||||
#CFLAGS += -Wunsuffixed-float-constants # # { float f=0.67; if(f==0.67) printf("y"); else printf("n"); } => n
|
||||
# ^ doesn't seem to work right? find_north does funny stuff
|
||||
|
||||
#CFLAGS += -Wtraditional-conversion # # prototype causes a conversion different than w/o prototype (?)
|
||||
# ^ real noisy
|
||||
|
||||
# CXX-only warnings
|
||||
override CXXFLAGS += -Wctor-dtor-privacy # # unusable class b/c everything private and no friends
|
||||
override CXXFLAGS += -Wdelete-non-virtual-dtor # # catches undefined behavior
|
||||
override CXXFLAGS += -Wold-style-cast # # C-style cast in C++ code
|
||||
override CXXFLAGS += -Woverloaded-virtual # # subclass shadowing makes parent impl's unavailable
|
||||
override CXXFLAGS += -Wsign-promo # # gcc did what spec requires, but probably not what you want
|
||||
override CXXFLAGS += -Wstrict-null-sentinel # # seems like a not-very-C++ thing to do? very unsure
|
||||
override CXXFLAGS += -Wsuggest-final-methods # # does what it sounds like
|
||||
override CXXFLAGS += -Wsuggest-final-types # # does what it sounds like
|
||||
override CXXFLAGS += -Wsuggest-override # # overridden virtual func w/out override keyword
|
||||
override CXXFLAGS += -Wuseless-cast # # pretty much what ya think here
|
||||
override CXXFLAGS += -Wzero-as-null-pointer-constant # use of 0 as NULL
|
||||
|
||||
# -Wc++-compat # # C/C++ compat issues
|
||||
# -Wc++11-compat # # C11 compat issues
|
||||
# -Wc++14-compat # # C14 compat issues
|
||||
# -Wconditionally-supported # # conditionally-supported (C++11 [intro.defs]) constructs (?)
|
||||
# -Weffc++ # violations of style guidelines from Meyers' Effective C++ books
|
||||
# -Wmultiple-inheritance # used to enforce coding conventions, does what you'd think
|
||||
# -Wnamespaces # used to enforce coding conventions, warn if namespace opened
|
||||
# -Wnoexcept # # (?) I think warns if missing noexcept
|
||||
# -Wnon-virtual-dtor # # something deeply c++, part of effc++
|
||||
# -Wsynth # legacy flag, g++ != cfront
|
||||
# -Wtemplates # used to enforce coding conventions, warn if new template
|
||||
# -Wvirtual-inheritance # used to enforce coding conventions, does what you'd think
|
||||
|
||||
# Fortran-only warnings
|
||||
# -Waliasing
|
||||
# -Wampersand
|
||||
# -Warray-temporaries
|
||||
# -Wc-binding-type
|
||||
# -Wcharacter-truncation
|
||||
# -Wcompare-reals
|
||||
# -Wconversion-extra
|
||||
# -Wfunction-elimination
|
||||
# -Wimplicit-interface
|
||||
# -Wimplicit-procedure
|
||||
# -Winteger-division
|
||||
# -Wintrinsic-shadow
|
||||
# -Wintrinsics-std
|
||||
# -Wreal-q-constant
|
||||
# -Wrealloc-lhs
|
||||
# -Wrealloc-lhs-all
|
||||
# -Wsurprising
|
||||
# -Wtabs
|
||||
# -Wtarget-lifetime
|
||||
# -Wunused-dummy-argument
|
||||
# -Wuse-without-only
|
||||
|
||||
# Objective-C(++)-only
|
||||
# -Wassign-intercept
|
||||
# -Wselector
|
||||
# -Wstrict-selector-match
|
||||
# -Wundeclared-selector
|
||||
|
||||
# END WARNINGS
|
||||
################################################################################
|
||||
|
||||
|
||||
# C/C++ Linter configuration
|
||||
UNCRUSTIFY := $(TOCK_USERLAND_BASE_DIR)/tools/uncrustify/uncrustify.sh
|
||||
|
||||
|
||||
# Dump configuration for verbose builds
|
||||
ifneq ($(V),)
|
||||
$(info )
|
||||
$(info **************************************************)
|
||||
$(info TOCK USERLAND BUILD SYSTEM -- VERBOSE BUILD)
|
||||
$(info **************************************************)
|
||||
$(info Config:)
|
||||
$(info GIT: $(shell git describe --always 2>&1))
|
||||
$(info $(TOOLCHAIN_cortex-m4)$(CC_cortex-m4) --version: $(shell $(TOOLCHAIN_cortex-m4)$(CC_cortex-m4) --version))
|
||||
ifneq ($(RISCV),)
|
||||
$(info $(TOOLCHAIN_rv32i)$(CC_rv32i) --version: $(shell $(TOOLCHAIN_rv32i)$(CC_rv32i) --version))
|
||||
endif
|
||||
$(info LAYOUT=$(LAYOUT))
|
||||
$(info MAKEFLAGS=$(MAKEFLAGS))
|
||||
$(info PACKAGE_NAME=$(PACKAGE_NAME))
|
||||
$(info TOCK_ARCHS=$(TOCK_ARCHS))
|
||||
$(info TOCK_TARGETS=$(TOCK_TARGETS))
|
||||
$(info TOCK_USERLAND_BASE_DIR=$(TOCK_USERLAND_BASE_DIR))
|
||||
$(info TOOLCHAIN=$(TOOLCHAIN))
|
||||
$(info **************************************************)
|
||||
$(info )
|
||||
endif
|
||||
|
||||
endif
|
@ -0,0 +1,91 @@
|
||||
################################################################################
|
||||
##
|
||||
## libtock-c helper functions and definitions for use by Tock makefiles.
|
||||
## Included by AppMakefile.mk and libtock's Makefile
|
||||
##
|
||||
################################################################################
|
||||
|
||||
# Ensure that this file is only included once.
|
||||
ifndef HELPERS_MAKEFILE
|
||||
HELPERS_MAKEFILE = 1
|
||||
|
||||
# http://stackoverflow.com/questions/10858261/abort-makefile-if-variable-not-set
|
||||
# Check that given variables are set and all have non-empty values,
|
||||
# die with an error otherwise.
|
||||
#
|
||||
# Params:
|
||||
# 1. Variable name(s) to test.
|
||||
# 2. (optional) Error message to print.
|
||||
check_defined = \
|
||||
$(strip $(foreach 1,$1, \
|
||||
$(call __check_defined,$1,$(strip $(value 2)))))
|
||||
__check_defined = \
|
||||
$(if $(value $1),, \
|
||||
$(error Undefined $1$(if $2, ($2))))
|
||||
|
||||
# Check for a ~/ at the beginning of a path variable (TOCK_USERLAND_BASE_DIR).
|
||||
# Make will not properly expand this.
|
||||
ifdef TOCK_USERLAND_BASE_DIR
|
||||
ifneq (,$(findstring BEGINNINGOFVARIABLE~/,BEGINNINGOFVARIABLE$(TOCK_USERLAND_BASE_DIR)))
|
||||
$(error Hi! Using "~" in Makefile variables is not supported. Use "$$(HOME)" instead)
|
||||
endif
|
||||
endif
|
||||
|
||||
# # Validate the the toolchain is new enough (known not to work for gcc <= 5.1)
|
||||
# CC_VERSION_MAJOR := $(shell $(CC) -dumpversion | cut -d '.' -f1)
|
||||
# ifeq (1,$(shell expr $(CC_VERSION_MAJOR) \>= 6))
|
||||
# # Opportunistically turn on gcc 6.0+ warnings since we're already version checking:
|
||||
# override CPPFLAGS += -Wduplicated-cond # if (p->q != NULL) { ... } else if (p->q != NULL) { ... }
|
||||
# override CPPFLAGS += -Wnull-dereference # deref of NULL (thought default if -fdelete-null-pointer-checks, in -Os, but no?)
|
||||
# else
|
||||
# ifneq (5,$(CC_VERSION_MAJOR))
|
||||
# $(info CC=$(CC))
|
||||
# $(info $$(CC) -dumpversion: $(shell $(CC) -dumpversion))
|
||||
# $(error Your compiler is too old. Need gcc version > 5.1)
|
||||
# endif
|
||||
# CC_VERSION_MINOR := $(shell $(CC) -dumpversion | cut -d '.' -f2)
|
||||
# ifneq (1,$(shell expr $(CC_VERSION_MINOR) \> 1))
|
||||
# $(info CC=$(CC))
|
||||
# $(info $$(CC) -dumpversion: $(shell $(CC) -dumpversion))
|
||||
# $(error Your compiler is too old. Need gcc version > 5.1)
|
||||
# endif
|
||||
# endif
|
||||
|
||||
|
||||
# Format check rule
|
||||
.PHONY: _format_check_unstaged
|
||||
_format_check_unstaged:
|
||||
$(Q)$(TOCK_USERLAND_BASE_DIR)/tools/check_unstaged.sh
|
||||
|
||||
#########################################################################################
|
||||
## Pretty-printing rules
|
||||
|
||||
# If environment variable V is non-empty, be verbose.
|
||||
ifneq ($(V),)
|
||||
Q=
|
||||
TRACE_DIR =
|
||||
TRACE_BIN =
|
||||
TRACE_DEP =
|
||||
TRACE_CC =
|
||||
TRACE_CXX =
|
||||
TRACE_LD =
|
||||
TRACE_AR =
|
||||
TRACE_AS =
|
||||
TRACE_LST =
|
||||
TRACE_E2T =
|
||||
ELF2TAB_ARGS += -v
|
||||
else
|
||||
Q=@
|
||||
TRACE_DIR = @echo " DIR " $@
|
||||
TRACE_BIN = @echo " BIN " $@
|
||||
TRACE_DEP = @echo " DEP " $<
|
||||
TRACE_CC = @echo " CC " $<
|
||||
TRACE_CXX = @echo " CXX " $<
|
||||
TRACE_LD = @echo " LD " $@
|
||||
TRACE_AR = @echo " AR " $@
|
||||
TRACE_AS = @echo " AS " $<
|
||||
TRACE_LST = @echo " LST " $<
|
||||
TRACE_E2T = @echo " E2T " $@
|
||||
endif
|
||||
|
||||
endif
|
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@ -0,0 +1,25 @@
|
||||
Copyright (c) 2016 The Tock Project Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
@ -0,0 +1,3 @@
|
||||
prepush:
|
||||
cd examples; ./format_all.sh
|
||||
|
@ -0,0 +1,50 @@
|
||||
################################################################################
|
||||
##
|
||||
## Makefile for loading applications onto a Tockloader compatible board.
|
||||
##
|
||||
################################################################################
|
||||
|
||||
$(call check_defined, BUILDDIR)
|
||||
$(call check_defined, PACKAGE_NAME)
|
||||
|
||||
TOCKLOADER ?= tockloader
|
||||
OPENOCD ?= openocd
|
||||
|
||||
# Upload programs over UART with tockloader.
|
||||
ifdef PORT
|
||||
TOCKLOADER_GENERAL_FLAGS += --port $(PORT)
|
||||
endif
|
||||
|
||||
# Setup specific commands for each board.
|
||||
ifeq ("$(TOCK_BOARD)","hail")
|
||||
PROGRAM = $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) install $<
|
||||
FLASH = $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) install --jlink $<
|
||||
|
||||
else ifeq ("$(TOCK_BOARD)","imix")
|
||||
# Change program region offset
|
||||
TOCKLOADER_INSTALL_FLAGS += --app-address 0x40000
|
||||
PROGRAM = $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) install $(TOCKLOADER_INSTALL_FLAGS) $<
|
||||
FLASH = $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) install $(TOCKLOADER_INSTALL_FLAGS) --jlink $<
|
||||
|
||||
else ifeq ("$(TOCK_BOARD)","ek-tm4c1294xl")
|
||||
FLASH = $(OPENOCD) -c "source [find board/ek-tm4c1294xl.cfg]; init; reset halt; flash write_image erase $< 0x00020000 bin; reset; shutdown"
|
||||
|
||||
else ifeq ("$(TOCK_BOARD)","nrf51dk")
|
||||
FLASH = $(TOCKLOADER) install --jlink --board nrf51dk $<
|
||||
|
||||
else ifeq ("$(TOCK_BOARD)","nrf52dk")
|
||||
FLASH = $(TOCKLOADER) install --jlink --board nrf52dk $<
|
||||
|
||||
endif
|
||||
|
||||
PROGRAM ?= @(echo "Cannot program over serial $<"; exit 1)
|
||||
FLASH ?= @(echo "Cannot flash $<"; exit 1)
|
||||
|
||||
.PHONY: program
|
||||
program: $(BUILDDIR)/$(PACKAGE_NAME).tab
|
||||
$(PROGRAM)
|
||||
|
||||
# Upload programs over JTAG
|
||||
.PHONY: flash
|
||||
flash: $(BUILDDIR)/$(PACKAGE_NAME).tab
|
||||
$(FLASH)
|
@ -0,0 +1,294 @@
|
||||

|
||||
[][slack]
|
||||
|
||||
Tock Userland
|
||||
=============
|
||||
|
||||
This directory contains libraries and example applications for developing
|
||||
Tock apps that sit above the kernel.
|
||||
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
1. If you have not yet done so, it might be a good idea to start with
|
||||
the [TockOS getting started
|
||||
guide](https://github.com/tock/tock/blob/master/doc/Getting_Started.md),
|
||||
which will lead you through the installation of some tools that
|
||||
will be useful for developing and deploying applications on
|
||||
TockOS. In particular, it will give you a rust environment
|
||||
(required to install `elf2tab`) and `tockloader`, which you need to
|
||||
deploy applications on most boards.
|
||||
|
||||
And it will of course give you a board with TockOS installed which
|
||||
you can use to run the applications found in this repository.
|
||||
|
||||
So, if you haven't been there before, just head over there until it
|
||||
sends you back here.
|
||||
|
||||
1. Clone this repository.
|
||||
|
||||
```
|
||||
$ git clone https://github.com/tock/libtock-c
|
||||
$ cd libtock-c
|
||||
```
|
||||
|
||||
1. The main requirement to build the C applications in this repository is having
|
||||
cross compilers for embedded targets. You will need an `arm-none-eabi`
|
||||
toolchain for Cortex-M targets.
|
||||
|
||||
**MacOS**:
|
||||
```
|
||||
$ brew tap ARMmbed/homebrew-formulae && brew update && brew install arm-none-eabi-gcc
|
||||
```
|
||||
|
||||
**Ubuntu (18.04LTS or later)**:
|
||||
```
|
||||
$ sudo apt install gcc-arm-none-eabi
|
||||
```
|
||||
|
||||
**Arch**:
|
||||
```
|
||||
$ sudo pacman -Syu arm-none-eabi-gcc arm-none-eabi-newlib
|
||||
```
|
||||
|
||||
**Fedora**:
|
||||
```
|
||||
$ sudo dnf install arm-none-eabi-newlib arm-none-eabi-gcc-cs
|
||||
```
|
||||
|
||||
2. Optional: libtock-c also includes support for building for ***RISC-V
|
||||
targets***. These are not included by default since obtaining the toolchain
|
||||
can be difficult (as of July 2022). You will need a RISC-V toolchain that
|
||||
supports rv32 targets (64 bit toolchains support rv32 if compiled with
|
||||
multilib support). Some toolchains that can work:
|
||||
|
||||
- riscv64-none-elf
|
||||
- riscv32-none-elf
|
||||
- riscv64-elf
|
||||
- riscv64-unknown-elf
|
||||
- riscv32-unknown-elf
|
||||
|
||||
To actually build for the RISC-V targets, add `RISCV=1` to the make command:
|
||||
|
||||
$ make RISCV=1
|
||||
|
||||
**MacOS**:
|
||||
```
|
||||
$ brew tap riscv/riscv && brew update && brew install riscv-gnu-toolchain
|
||||
```
|
||||
|
||||
**Ubuntu (21.10 or later)**:
|
||||
```
|
||||
$ sudo apt install gcc-riscv64-unknown-elf picolibc-riscv64-unknown-elf
|
||||
```
|
||||
|
||||
**Ubuntu (21.04 or earlier)**:
|
||||
|
||||
Unfortunately, older Ubuntu does not provide a package for RISC-V libc. We
|
||||
have created a .deb file you can use to install a suitable libc based on
|
||||
newlib:
|
||||
```
|
||||
$ wget http://cs.virginia.edu/~bjc8c/archive/newlib_3.3.0-1_amd64.deb
|
||||
$ sudo dpkg -i newlib_3.3.0-1_amd64.deb
|
||||
```
|
||||
|
||||
If you would rather compile your own newlib-based libc, follow the steps
|
||||
below. Section [newlib-nano](newlib-nano) describes some extra config options
|
||||
to build a size optimised newlib.
|
||||
```
|
||||
# Download newlib 3.3 from https://sourceware.org/newlib/
|
||||
$ wget ftp://sourceware.org/pub/newlib/newlib-3.3.0.tar.gz
|
||||
$ tar -xvf newlib-3.3.0.tar.gz
|
||||
$ cd newlib-3.3.0
|
||||
# Disable stdlib for building
|
||||
$ export CFLAGS=-nostdlib
|
||||
# Run configure
|
||||
$ ./configure --disable-newlib-supplied-syscalls --with-gnu-ld --with-newlib --enable-languages=c --target=riscv64-unknown-elf --host=x86 --disable-multi-lib --prefix /usr
|
||||
# Build and then install
|
||||
$ make -j8
|
||||
$ sudo make install
|
||||
```
|
||||
|
||||
Alternatively, you may use a pre-compiled toolchain that we created with
|
||||
Crosstool-NG.
|
||||
```
|
||||
$ wget http://cs.virginia.edu/~bjc8c/archive/gcc-riscv64-unknown-elf-8.3.0-ubuntu.zip
|
||||
$ unzip gcc-riscv64-unknown-elf-8.3.0-ubuntu.zip
|
||||
# add gcc-riscv64-unknown-elf-8.3.0-ubuntu/bin to your `$PATH` variable.
|
||||
```
|
||||
|
||||
**Arch**:
|
||||
```
|
||||
$ sudo pacman -Syu riscv64-elf-gcc riscv32-elf-newlib arm-none-eabi-newlib riscv64-elf-newlib
|
||||
```
|
||||
|
||||
**Fedora**:
|
||||
|
||||
**dnf** does not contain the `riscv-gnu-toolchain`, an alternative is to
|
||||
compile from source. Start with some of the tools we need to compile the
|
||||
source.
|
||||
```
|
||||
$ sudo dnf install make automake gcc gcc-c++ kernel-devel texinfo expat expat-devel
|
||||
$ sudo dnf group install "Development Tools" "C Development Tools and Libraries"
|
||||
```
|
||||
Get `riscv-gnu-toolchain`, [summarised instructions as stated
|
||||
here](https://github.com/riscv-collab/riscv-gnu-toolchain/blob/master/README.md)
|
||||
```
|
||||
$ git clone https://github.com/riscv/riscv-gnu-toolchain
|
||||
$ cd riscv-gnu-toolchain/
|
||||
```
|
||||
**Note: add /opt/riscv/bin to your PATH**, then,
|
||||
```
|
||||
$ ./configure --prefix=/opt/riscv --enable-multilib
|
||||
```
|
||||
`--enable-multilib` ensures that "the multilib compiler will have the prefix
|
||||
riscv64-unknown-elf- or riscv64-unknown-linux-gnu- but will be able to target
|
||||
both 32-bit and 64-bit systems."
|
||||
```
|
||||
$ sudo make [might need elevated privileges writing to `/opt/`]
|
||||
```
|
||||
additionally, with
|
||||
```
|
||||
$ sudo make linux
|
||||
```
|
||||
you can also build `riscv64-unknown-linux-gnu`, which can be useful with tock
|
||||
where `riscv64-unknown-linux-gnu-objcopy` is used.
|
||||
|
||||
After the the source has been compiled and copied to `/opt/riscv` and
|
||||
`/opt/riscv/bin`has appended to the PATH, the toolchain is ready to be used.
|
||||
|
||||
|
||||
**newlib-nano**:
|
||||
|
||||
newlib can require a large amount of memory, especially for printing.
|
||||
If this is a concern you can instead use a more size optimised version.
|
||||
As of August 2020 there are a few options for this.
|
||||
|
||||
- See if the version of newlib from your distro already has the flags below
|
||||
enabled. If it does it's already size optimsed.
|
||||
- See if your distro pacakges a newlib-nano (Debian does this) that will
|
||||
already include the flags below.
|
||||
- See if your distro packages picolibc, which is a optimised fork of newlib.
|
||||
- You can compile newlib with these extra flags:
|
||||
```
|
||||
--enable-newlib-reent-small \
|
||||
--disable-newlib-fvwrite-in-streamio \
|
||||
--disable-newlib-fseek-optimization \
|
||||
--disable-newlib-wide-orient \
|
||||
--enable-newlib-nano-malloc \
|
||||
--disable-newlib-unbuf-stream-opt \
|
||||
--enable-lite-exit \
|
||||
--enable-newlib-global-atexit \
|
||||
--enable-newlib-nano-formatted-io
|
||||
```
|
||||
|
||||
3. Optional: libtock-c also includes support for building RISC-V targets with
|
||||
the LLVM clang compiler. If you have a compatible clang toolchain, you can
|
||||
add `CLANG=1` to the make command to use clang instead of the default GCC.
|
||||
|
||||
$ make RISCV=1 CLANG=1
|
||||
|
||||
This support is only included for RISC-V targets as Cortex-M targets require
|
||||
the FDPIC support only present in GCC.
|
||||
|
||||
4. You will also need an up-to-date version of
|
||||
[elf2tab](https://crates.io/crates/elf2tab). The build system will install
|
||||
and update this automatically for you, but you'll need Rust's
|
||||
[cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html)
|
||||
installed. If you have followed the getting started guide, everything should
|
||||
be in place.
|
||||
|
||||
5. You will also likely need [Tockloader](https://github.com/tock/tockloader), a
|
||||
tool for programming apps onto boards. If you haven't installed it
|
||||
during the TockOS getting started guide:
|
||||
|
||||
MacOS:
|
||||
```
|
||||
$ pip3 install tockloader
|
||||
```
|
||||
|
||||
Ubuntu:
|
||||
```
|
||||
$ pip3 install tockloader --user
|
||||
```
|
||||
|
||||
Compiling and Running Applications
|
||||
----------------------------------
|
||||
|
||||
To compile all the examples, switch to the `examples` directory and execute the
|
||||
build script:
|
||||
|
||||
$ cd examples
|
||||
$ ./build_all.sh
|
||||
|
||||
This will install `elf2tab` if it is not yet installed and compile all the
|
||||
examples for cortex-m0, cortex-m3, cortex-m4, cortex-m7, and rv32imac. It does
|
||||
this because the compiler emits slightly (or significantly) different
|
||||
instructions for each variant. When installing the application, `tockloader`
|
||||
will select the correct version for the architecture of the board being
|
||||
programmed.
|
||||
|
||||
The build process will ultimately create a `tab` file (a "Tock Application
|
||||
Bundle") for each example application. The `tab` contains the executable code
|
||||
for the supported architectures and can be deployed to a board using
|
||||
`tockloader`. For example to one of the Nordic development boards:
|
||||
|
||||
```
|
||||
$ tockloader install --board nrf52dk --jlink blink/build/blink.tab
|
||||
Installing apps on the board...
|
||||
Using known arch and jtag-device for known board nrf52dk
|
||||
Finished in 2.567 seconds
|
||||
```
|
||||
|
||||
You can remove an application with
|
||||
|
||||
$ tockloader uninstall --board nrf52dk --jlink blink
|
||||
|
||||
or remove all installed applications with
|
||||
|
||||
$ tockloader uninstall --board nrf52dk --jlink
|
||||
|
||||
Tock applications are designed to be generic and run on any Tock-compatible
|
||||
board. However, compiled applications typically depend on specific drivers,
|
||||
which not all boards provide. For example, some applications expect an IEEE
|
||||
802.15.4 radio interface which not all boards support. If you load an
|
||||
application onto a board that does not support every driver/system call it uses,
|
||||
some system calls will return error codes (`ENODEVICE` or `ENOSUPPORT`).
|
||||
|
||||
Next Steps
|
||||
----------
|
||||
|
||||
The next step is to read the [overview](doc/overview.md) that describes how
|
||||
applications in TockOS are structured and then look at some of the examples in
|
||||
detail. The description of the [compilation environment](doc/compilation.md) may
|
||||
also be of interest.
|
||||
|
||||
[slack]: https://join.slack.com/t/tockos/shared_invite/enQtNDE5ODQyNDU4NTE1LWVjNTgzMTMwYzA1NDI1MjExZjljMjFmOTMxMGIwOGJlMjk0ZTI4YzY0NTYzNWM0ZmJmZGFjYmY5MTJiMDBlOTk
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
Contributions
|
||||
-------------
|
||||
|
||||
We welcome contributions from all. We use the bors-ng bot to manage, approve,
|
||||
and merge PRs. In short, when someone replies `bors r+`, your PR has been
|
||||
approved and will be automatically merged. If a maintainer replies `bors
|
||||
delegate+`, then you have been granted the authority to mark your own PR for
|
||||
approval (usually this will happen if there are some trivial changes required).
|
||||
For a full list of bors commands, [see the bors
|
||||
documentation](https://bors.tech/documentation/).
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
@ -0,0 +1,191 @@
|
||||
################################################################################
|
||||
##
|
||||
## libtock-c library shared makefile. Included by library makefiles to build
|
||||
## libraries for use with libtock-c apps.
|
||||
##
|
||||
################################################################################
|
||||
|
||||
# The first target Make finds is its default. So this line needs to be first to
|
||||
# specify `all` as our default rule.
|
||||
all:
|
||||
|
||||
# Build settings.
|
||||
include $(TOCK_USERLAND_BASE_DIR)/Configuration.mk
|
||||
|
||||
# Helper functions.
|
||||
include $(TOCK_USERLAND_BASE_DIR)/Helpers.mk
|
||||
|
||||
$(call check_defined, LIBNAME)
|
||||
$(call check_defined, $(LIBNAME)_DIR)
|
||||
$(call check_defined, $(LIBNAME)_SRCS)
|
||||
|
||||
ifeq ($(strip $($(LIBNAME)_SRCS)),)
|
||||
$(error Library "$(LIBNAME)" has no SRCS?)
|
||||
endif
|
||||
|
||||
# directory for built output
|
||||
$(LIBNAME)_BUILDDIR ?= $($(LIBNAME)_DIR)/build
|
||||
|
||||
# Handle complex paths.
|
||||
#
|
||||
# Okay, so this merits some explanation:
|
||||
#
|
||||
# Our build system aspires to put everything in build/ directories, this means
|
||||
# that we have to match the path of source files (foo.c) to output directories
|
||||
# (build/<arch>/foo.o). That's easy enough if all the source files are in the
|
||||
# same directory, but restricts applications and libraries to a flat file
|
||||
# structure.
|
||||
#
|
||||
# The current solution we employ is built on make's VPATH variable, which is a
|
||||
# list of directories to search for dependencies, e.g.
|
||||
#
|
||||
# VPATH = foo/ ../bar/
|
||||
# somerule: dependency.c
|
||||
#
|
||||
# Will find any of ./dependency.c, foo/dependency.c, or ../bar/dependency.c
|
||||
# We leverage this by flattening the list of SRCS to remove all path
|
||||
# information and adding all the paths from the SRCS to the VPATH, this means
|
||||
# we can write rules as-if all the SRCS were in a flat directory.
|
||||
#
|
||||
# The obvious pitfall here is what happens when multiple directories hold a
|
||||
# source file of the same name. However, both libnrf and mbed are set up to
|
||||
# use VPATH without running into that problem, which gives some pretty serious
|
||||
# hope that it won't be an issue in practice. The day is actually is a problem,
|
||||
# we can revisit this, but the only solution I can think of presently is
|
||||
# another layer of macros that generates the build rules for each path in SRCS,
|
||||
# which is a pretty hairy sounding proposition
|
||||
|
||||
$(LIBNAME)_SRCS_FLAT := $(notdir $($(LIBNAME)_SRCS))
|
||||
$(LIBNAME)_SRCS_DIRS := $(sort $(dir $($(LIBNAME)_SRCS))) # sort removes duplicates
|
||||
|
||||
# Only use vpath for certain types of files
|
||||
# But must be a global list
|
||||
VPATH_DIRS += $($(LIBNAME)_SRCS_DIRS)
|
||||
vpath %.s $(VPATH_DIRS)
|
||||
vpath %.c $(VPATH_DIRS)
|
||||
vpath %.cc $(VPATH_DIRS)
|
||||
vpath %.cpp $(VPATH_DIRS)
|
||||
vpath %.cxx $(VPATH_DIRS)
|
||||
|
||||
# Now, VPATH allows _make_ to find all the sources, but gcc needs to be told
|
||||
# how to find all of the headers. We do this by `-I`'ing any folder that had a
|
||||
# LIB_SRC and has any .h files in it. We also check the common convention of
|
||||
# headers in an include/ folder (both in and adjacent to src/) while we're at it
|
||||
define LIB_HEADER_INCLUDES
|
||||
ifneq ($$(wildcard $(1)/*.h),"")
|
||||
override CPPFLAGS += -I$(1)
|
||||
endif
|
||||
ifneq ($$(wildcard $(1)/include/*.h),"")
|
||||
override CPPFLAGS += -I$(1)/include
|
||||
endif
|
||||
ifneq ($$(wildcard $(1)/../include/*.h),"")
|
||||
override CPPFLAGS += -I$(1)/../include
|
||||
endif
|
||||
endef
|
||||
# uncomment to print generated rules
|
||||
# $(info $(foreach hdrdir,$($(LIBNAME)_SRCS_DIRS),$(call LIB_HEADER_INCLUDES,$(hdrdir))))
|
||||
# actually generate the rules
|
||||
$(foreach hdrdir,$($(LIBNAME)_SRCS_DIRS),$(eval $(call LIB_HEADER_INCLUDES,$(hdrdir))))
|
||||
|
||||
# Rules to generate libraries for a given Architecture
|
||||
# These will be used to create the different architecture versions of LibNRFSerialization
|
||||
# Argument $(1) is the Architecture (e.g. cortex-m0) to build for
|
||||
define LIB_RULES
|
||||
|
||||
$$($(LIBNAME)_BUILDDIR)/$(1):
|
||||
$$(TRACE_DIR)
|
||||
$$(Q)mkdir -p $$@
|
||||
|
||||
$$($(LIBNAME)_BUILDDIR)/$(1)/%.o: %.c | $$($(LIBNAME)_BUILDDIR)/$(1)
|
||||
$$(TRACE_CC)
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(CC_$(1)) $$(CFLAGS) $$(CFLAGS_$(1)) $$(CPPFLAGS) $$(CPPFLAGS_$(1)) -MF"$$(@:.o=.d)" -MG -MM -MP -MT"$$(@:.o=.d)@" -MT"$$@" "$$<"
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(CC_$(1)) $$(CFLAGS) $$(CFLAGS_$(1)) $$(CPPFLAGS) $$(CPPFLAGS_$(1)) -c -o $$@ $$<
|
||||
|
||||
$$($(LIBNAME)_BUILDDIR)/$(1)/%.o: %.S | $$($(LIBNAME)_BUILDDIR)/$(1)
|
||||
$$(TRACE_AS)
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(AS) $$(ASFLAGS) $$(CPPFLAGS) $$(CPPFLAGS_$(1)) -c -o $$@ $$<
|
||||
|
||||
$(LIBNAME)_OBJS_$(1) += $$(patsubst %.s,$$($(LIBNAME)_BUILDDIR)/$(1)/%.o,$$(filter %.s, $$($(LIBNAME)_SRCS_FLAT)))
|
||||
$(LIBNAME)_OBJS_$(1) += $$(patsubst %.c,$$($(LIBNAME)_BUILDDIR)/$(1)/%.o,$$(filter %.c, $$($(LIBNAME)_SRCS_FLAT)))
|
||||
$(LIBNAME)_OBJS_$(1) += $$(patsubst %.cc,$$($(LIBNAME)_BUILDDIR)/$(1)/%.o,$$(filter %.cc, $$($(LIBNAME)_SRCS_FLAT)))
|
||||
$(LIBNAME)_OBJS_$(1) += $$(patsubst %.cpp,$$($(LIBNAME)_BUILDDIR)/$(1)/%.o,$$(filter %.cpp, $$($(LIBNAME)_SRCS_FLAT)))
|
||||
$(LIBNAME)_OBJS_$(1) += $$(patsubst %.cxx,$$($(LIBNAME)_BUILDDIR)/$(1)/%.o,$$(filter %.cxx, $$($(LIBNAME)_SRCS_FLAT)))
|
||||
|
||||
# Dependency rules for picking up header changes
|
||||
-include $$($(LIBNAME)_OBJS_$(1):.o=.d)
|
||||
|
||||
# Useful debugging
|
||||
# $$(info -----------------------------------------------------)
|
||||
# $$(info $(LIBNAME) $(1))
|
||||
# $$(info $(LIBNAME)_SRCS: $$($(LIBNAME)_SRCS))
|
||||
# $$(info $(LIBNAME)_SRCS_FLAT: $$($(LIBNAME)_SRCS_FLAT))
|
||||
# $$(info VPATH: $$(VPATH))
|
||||
# $$(info $(LIBNAME)_OBJS_$(1): $$($(LIBNAME)_OBJS_$(1)))
|
||||
# $$(info =====================================================)
|
||||
|
||||
$$($(LIBNAME)_BUILDDIR)/$(1)/$(LIBNAME).a: $$($(LIBNAME)_OBJS_$(1)) | $$($(LIBNAME)_BUILDDIR)/$(1)
|
||||
$$(TRACE_AR)
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(AR) rc $$@ $$^
|
||||
$$(Q)$$(TOOLCHAIN_$(1))$$(RANLIB) $$@
|
||||
|
||||
# If we're building this library as part of a bigger build, add ourselves to
|
||||
# the list of libraries
|
||||
#
|
||||
# Ahh.. make. By default, the RHS of variables aren't expanded at all until the
|
||||
# variable is _used_ ("lazy set", "="), this means that LIBNAME will have
|
||||
# changed by the time the variable is evaluated. We want immediate set (":="),
|
||||
# but we'd also like to append ("+=") to grow the list. Append chooses between
|
||||
# lazy or immediate set based on how the variable was previously set (yes,
|
||||
# that's right, the RHS evaluation depends on the LHS type - make was ahead of
|
||||
# it's time! :D), and defaults to lazy set if the variable is undefined at the
|
||||
# first append. So, we force it to immediate set. Lovely.
|
||||
ifndef LIBS_$(1)
|
||||
LIBS_$(1) :=
|
||||
endif
|
||||
LIBS_$(1) += $$($(LIBNAME)_BUILDDIR)/$(1)/$(LIBNAME).a
|
||||
|
||||
endef
|
||||
|
||||
# uncomment to print generated rules
|
||||
# $(info $(foreach platform,$(TOCK_ARCHS), $(call LIB_RULES,$(call ARCH_FN,$(platform)))))
|
||||
# actually generate the rules for each architecture
|
||||
$(foreach arch,$(TOCK_ARCHS),$(eval $(call LIB_RULES,$(arch))))
|
||||
|
||||
# add each architecture as a target
|
||||
.PHONY: all
|
||||
all: $(foreach arch, $(TOCK_ARCHS),$($(LIBNAME)_BUILDDIR)/$(arch)/$(LIBNAME).a)
|
||||
|
||||
|
||||
# Force LIBNAME to be expanded now
|
||||
define CLEAN_RULE
|
||||
.PHONY: clean
|
||||
clean::
|
||||
rm -Rf $(1)
|
||||
endef
|
||||
$(eval $(call CLEAN_RULE,$($(LIBNAME)_BUILDDIR)))
|
||||
|
||||
|
||||
# Rules for running the C linter
|
||||
$(LIBNAME)_FORMATTED_FILES := $(patsubst %.c,$($(LIBNAME)_BUILDDIR)/format/%.uncrustify,$(filter %.c, $($(LIBNAME)_SRCS_FLAT)))
|
||||
$(LIBNAME)_FORMATTED_FILES += $(patsubst %.cc,$($(LIBNAME)_BUILDDIR)/format/%.uncrustify,$(filter %.cc, $($(LIBNAME)_SRCS_FLAT)))
|
||||
$(LIBNAME)_FORMATTED_FILES += $(patsubst %.cpp,$($(LIBNAME)_BUILDDIR)/format/%.uncrustify,$(filter %.cpp, $($(LIBNAME)_SRCS_FLAT)))
|
||||
$(LIBNAME)_FORMATTED_FILES += $(patsubst %.cxx,$($(LIBNAME)_BUILDDIR)/format/%.uncrustify,$(filter %.cxx, $($(LIBNAME)_SRCS_FLAT)))
|
||||
|
||||
$($(LIBNAME)_BUILDDIR)/format:
|
||||
@mkdir -p $@
|
||||
|
||||
.PHONY: fmt format
|
||||
fmt format:: $($(LIBNAME)_FORMATTED_FILES)
|
||||
|
||||
$($(LIBNAME)_BUILDDIR)/format/%.uncrustify: %.c | _format_check_unstaged
|
||||
$(Q)$(UNCRUSTIFY) -f $< -o $@
|
||||
$(Q)cmp -s $< $@ || (if [ "$$CI" = "true" ]; then diff -y $< $@; rm $@; exit 1; else cp $@ $<; fi)
|
||||
$($(LIBNAME)_BUILDDIR)/format/%.uncrustify: %.cc | _format_check_unstaged
|
||||
$(Q)$(UNCRUSTIFY) -f $< -o $@
|
||||
$(Q)cmp -s $< $@ || (if [ "$$CI" = "true" ]; then diff -y $< $@; rm $@; exit 1; else cp $@ $<; fi)
|
||||
$($(LIBNAME)_BUILDDIR)/format/%.uncrustify: %.cpp | _format_check_unstaged
|
||||
$(Q)$(UNCRUSTIFY) -f $< -o $@
|
||||
$(Q)cmp -s $< $@ || (if [ "$$CI" = "true" ]; then diff -y $< $@; rm $@; exit 1; else cp $@ $<; fi)
|
||||
$($(LIBNAME)_BUILDDIR)/format/%.uncrustify: %.cxx | _format_check_unstaged
|
||||
$(Q)$(UNCRUSTIFY) -f $< -o $@
|
||||
$(Q)cmp -s $< $@ || (if [ "$$CI" = "true" ]; then diff -y $< $@; rm $@; exit 1; else cp $@ $<; fi)
|
@ -0,0 +1,19 @@
|
||||
# List of commit statuses that must pass on the merge commit before it is
|
||||
# pushed to master.
|
||||
status = [
|
||||
"ci-format (ubuntu-20.04)",
|
||||
"ci-build (ubuntu-20.04)",
|
||||
]
|
||||
|
||||
# List of PR labels that may not be attached to a PR when it is r+-ed.
|
||||
block_labels = [
|
||||
"blocked",
|
||||
]
|
||||
|
||||
# Number of seconds from when a merge commit is created to when its statuses
|
||||
# must pass. (Default = 3600).
|
||||
#timeout_sec = 7200
|
||||
|
||||
# If set to true, and if the PR branch is on the same repository that bors-ng
|
||||
# itself is on, the branch will be deleted.
|
||||
delete_merged_branches = true
|
@ -0,0 +1,167 @@
|
||||
Compilation Environment
|
||||
=======================
|
||||
|
||||
Tock aims to provide a build environment that is easy for application authors
|
||||
to integrate with. Check out the [examples](../examples) folder for
|
||||
sample applications. The Tock userland build system will automatically build
|
||||
with all of the correct flags and generate TABs for all supported Tock
|
||||
architectures.
|
||||
|
||||
To leverage the Tock build system, you must:
|
||||
|
||||
1. Set `TOCK_USERLAND_BASE_DIR` to the path to the Tock userland.
|
||||
2. `include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk`.
|
||||
|
||||
This `include` should be the _last_ line of the Makefile for most applications.
|
||||
|
||||
In addition, you must specify the sources for your application:
|
||||
|
||||
- `C_SRCS`: A list of C files to compile.
|
||||
- `CXX_SRCS`: A list of C++ files to compile.
|
||||
- `AS_SRCS`: A list of assembly files to compile.
|
||||
- `EXTERN_LIBS`: A list of directories for libraries [**compiled for Tock**](#compiling-libraries-for-tock).
|
||||
|
||||
## Customizing the build
|
||||
|
||||
### Flags
|
||||
|
||||
The build system respects all of the standard `CFLAGS` (C only), `CXXFLAGS`
|
||||
(C++ only), `CPPFLAGS` (C and C++), `ASFLAGS` (asm only).
|
||||
|
||||
By default, if you run something like `make CPPFLAGS=-Og`, make will use _only_
|
||||
the flags specified on the command line, but that means that Tock would lose all
|
||||
of its PIC-related flags. For that reason, Tock specifies all variables using
|
||||
make's [override directive](https://www.gnu.org/software/make/manual/html_node/Override-Directive.html).
|
||||
|
||||
If you wish to set additional flags in your application Makefiles, you must also
|
||||
use `override`, or they will be ignored. That is, in your Makefile you must write
|
||||
`override CPPFLAGS += -Og` rather than just `CPPFLAGS += -Og`.
|
||||
|
||||
If you are adding supplemental flags, you can put them anywhere. If you want to
|
||||
override Tock defaults, you'll need to place these _after_ the `include` directive
|
||||
in your Makefile.
|
||||
|
||||
### Application configuration
|
||||
|
||||
Several Tock-specific variables are also useful:
|
||||
|
||||
- `STACK_SIZE`: The minimum application stack size.
|
||||
- `APP_HEAP_SIZE`: The minimum heap size for your application.
|
||||
- `KERNEL_HEAP_SIZE`: The minimum grant size for your application.
|
||||
- `PACKAGE_NAME`: The name for your application. Defaults to current folder.
|
||||
|
||||
### Advanced
|
||||
|
||||
If you want to see a verbose build that prints all the commands as run, simply
|
||||
run `make V=1`.
|
||||
|
||||
The build system is broken across three files in the `libtock-c` repo:
|
||||
|
||||
- `Configuration.mk`: Sets most variables used.
|
||||
- `Helpers.mk`: Generic rules and functions to support the build.
|
||||
- `AppMakefile.mk`: Includes the above files and supplies build recipes.
|
||||
|
||||
Applications wishing to define their own build rules can include only the
|
||||
`Configuration.mk` file to ensure all of the flags needed for Tock applications
|
||||
are included.
|
||||
|
||||
## Compiling Libraries for Tock
|
||||
|
||||
Libraries used by Tock need all of the same position-independent build flags as
|
||||
the final application. As Tock builds for all supported architectures by
|
||||
default, libraries should include images for each supported Tock architecture.
|
||||
|
||||
### Let Tock do the work: TockLibrary.mk
|
||||
|
||||
As the Tock build requirements (PIC, multiple architectures) are fairly complex,
|
||||
Tock provides a Makefile that will ensure everything is set up correctly and
|
||||
generate build rules for you. An example Makefile for `libexample`:
|
||||
|
||||
> **libexample/Makefile**
|
||||
```make
|
||||
# Base definitions
|
||||
TOCK_USERLAND_BASE_DIR ?= ..
|
||||
LIBNAME := libexample
|
||||
|
||||
# Careful! Must be a path that resolves correctly **from where make is invoked**
|
||||
#
|
||||
# If you are only ever compiling a standalone library, then it's fine to simply set
|
||||
$(LIBNAME)_DIR := .
|
||||
#
|
||||
# If you will be asking applications to rebuild this library (see the development
|
||||
# section below), then you'll need to ensure that this directory is still correct
|
||||
# when invoked from inside the application folder.
|
||||
#
|
||||
# Tock accomplishes this for in-tree libraries by having all makefiles
|
||||
# conditionally set the TOCK_USERLAND_BASE_DIR variable, so that there
|
||||
# is a common relative path everywhere.
|
||||
$(LIBNAME)_DIR := $(TOCK_USERLAND_BASE_DIR)/$(LIBNAME)
|
||||
|
||||
# Grab all relevant source files. You can list them directly:
|
||||
$(LIBNAME)_SRCS := \
|
||||
$($LIBNAME)_DIR)\libexample.c \
|
||||
$($LIBNAME)_DIR)\libexample_helper.c \
|
||||
$($LIBNAME)_DIR)\subfolders_are_fine\otherfile.c
|
||||
|
||||
# Or let make find them automatically:
|
||||
$(LIBNAME)_SRCS := \
|
||||
$(wildcard $($(LIBNAME)_DIR)/*.c) \
|
||||
$(wildcard $($(LIBNAME)_DIR)/*.cxx) \ # or .cpp or .cc
|
||||
$(wildcard $($(LIBNAME)_DIR)/*.s)
|
||||
|
||||
include $(TOCK_USERLAND_BASE_DIR)/TockLibrary.mk
|
||||
```
|
||||
|
||||
> __Note! `:=` is NOT the same as `=` in make. You must use `:=`.__
|
||||
|
||||
### Developing (building) libraries concurrently with applications
|
||||
|
||||
When developing a library, often it's useful to have the library rebuild automatically
|
||||
as part of the application build. Assuming that your library is using `TockLibrary.mk`,
|
||||
you can simply include the library's Makefile in your application's Makefile:
|
||||
|
||||
```make
|
||||
include $(TOCK_USERLAND_BASE_DIR)/libexample/Makefile
|
||||
include ../../AppMakefile.mk
|
||||
```
|
||||
|
||||
**Example:** We don't have an in-tree example of a single app that rebuilds
|
||||
a dedicated library in the Tock repository, but libtock is effectively treated
|
||||
this way as its Makefile is
|
||||
[included by AppMakefile.mk](../AppMakefile.mk#L17).
|
||||
|
||||
### Pre-built libraries
|
||||
|
||||
You can also include pre-built libraries, but recall that Tock supports multiple
|
||||
architectures, which means you must supply a pre-built image for each.
|
||||
|
||||
Pre-built libraries must adhere to the following folder structure:
|
||||
|
||||
```
|
||||
For the library "example"
|
||||
|
||||
libexample/ <-- Folder name must match library name
|
||||
├── Makefile.app <-- Optional additional rules to include when building apps
|
||||
├── build
|
||||
│ ├── cortex-m0 <-- Architecture names match gcc's -mcpu= flag
|
||||
│ │ └── libexample.a <-- Library name must match folder name
|
||||
│ └── cortex-m4
|
||||
│ └── libexample.a <-- Library name must match folder name
|
||||
│
|
||||
└── root_header.h <-- The root directory will always be added to include path
|
||||
└── include <-- An include/ directory will be added too if it exists
|
||||
└── example.h
|
||||
```
|
||||
|
||||
To include a pre-built library, add the _path_ to the root folder to the
|
||||
variable `EXTERN_LIBS` in your application Makefile, e.g.
|
||||
`EXTERN_LIBS += ../../libexample`.
|
||||
|
||||
**Example:** In the Tock repository, lua53
|
||||
[ships a pre-built archive](../lua53/build/cortex-m4).
|
||||
|
||||
### Manually including libraries
|
||||
|
||||
To manually include an external library, add the library to each `LIBS_$(arch)`
|
||||
(i.e. `LIBS_cortex-m0`) variable. You can include header paths using the
|
||||
standard search mechanisms (i.e. `CPPFLAGS += -I<path>`).
|
@ -0,0 +1,99 @@
|
||||
Overview
|
||||
========
|
||||
|
||||
The bulk of Tock applications are written in C.
|
||||
|
||||
## Entry Point
|
||||
|
||||
Applications written in C that compile against libtock should define a `main`
|
||||
method with the following signature:
|
||||
|
||||
```c
|
||||
int main(void);
|
||||
```
|
||||
|
||||
Applications **should** return 0 from `main`. Returning non-zero is undefined and the
|
||||
behavior may change in future versions of `libtock`.
|
||||
Today, `main` is called from `_start` and includes an implicit `while()` loop:
|
||||
|
||||
```c
|
||||
void _start(void* text_start, void* mem_start, void* memory_len, void* app_heap_break) {
|
||||
main();
|
||||
while (1) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Applications should set up a series of event subscriptions in their `main`
|
||||
method and then return.
|
||||
|
||||
## Stack and Heap
|
||||
|
||||
Applications can specify their required stack and heap sizes by defining the
|
||||
make variables `STACK_SIZE` and `APP_HEAP_SIZE`, which default to 2K and 1K
|
||||
respectively as of this writing.
|
||||
|
||||
`libtock` will set the stack pointer during startup. To allow each application
|
||||
to set its own stack size, the linker script expects a symbol `STACK_SIZE` to
|
||||
be defined. The Tock build system will define this symbol during linking, using
|
||||
the make variable `STACK_SIZE`. A consequence of this technique is that
|
||||
changing the stack size requires that any source file also be touched so that
|
||||
the app will re-link.
|
||||
|
||||
## Libraries
|
||||
|
||||
Application code does not need to stand alone, libraries are available that can
|
||||
be utilized!
|
||||
|
||||
### Newlib
|
||||
Application code written in C has access to most of the [C standard
|
||||
library](https://en.wikipedia.org/wiki/C_standard_library) which is implemented
|
||||
by [Newlib](https://en.wikipedia.org/wiki/Newlib). Newlib is focused on
|
||||
providing capabilities for embedded systems. It provides interfaces such as
|
||||
`printf`, `malloc`, and `memcpy`. Most, but not all features of the standard
|
||||
library are available to applications. The built configuration of Newlib is
|
||||
specified in [build.sh](../userland/newlib/build.sh).
|
||||
|
||||
### libtock
|
||||
In order to interact with the Tock kernel, application code can use the
|
||||
`libtock` library. The majority of `libtock` are wrappers for interacting
|
||||
with Tock drivers through system calls. They provide the user a meaningful
|
||||
function name and arguments and then internally translate these into a
|
||||
`command`, `subscribe`, etc. Where it makes sense, the libraries also provide
|
||||
a synchronous interface to a driver using an internal callback and `yield_for`
|
||||
(example:
|
||||
[`tmp006_read_sync`](https://github.com/tock/tock/blob/master/userland/libtock/tmp006.c#L19))
|
||||
|
||||
`libtock` also provides the startup code for applications
|
||||
([`crt0.c`](../userland/libtock/crt0.c)),
|
||||
an implementation for the system calls
|
||||
([`tock.c`](../userland/libtock/tock.c)),
|
||||
and pin definitions for platforms.
|
||||
|
||||
### libc++
|
||||
Provides support for C++ apps. See `examples/cxx_hello`.
|
||||
|
||||
### libnrfserialization
|
||||
Provides a pre-compiled library for using the Nordic nRF serialization library
|
||||
for writing BLE apps.
|
||||
|
||||
### lua53
|
||||
Provides support for running a lua runtime as a Tock app. See
|
||||
`examples/lua-hello`.
|
||||
|
||||
## Style & Format
|
||||
|
||||
We try to keep a consistent style in mainline userland code. For C/C++, we use
|
||||
[uncrustify](https://github.com/uncrustify/uncrustify). High level:
|
||||
|
||||
- Two space character indents.
|
||||
- Braces on the same line.
|
||||
- Spaces around most operators.
|
||||
|
||||
For details, see the [configuration](../userland/tools/uncrustify).
|
||||
|
||||
Travis will automatically check formatting. You can format code locally using
|
||||
`make format`, or check the whole codebase with
|
||||
[format_all.sh](../userland/examples/format_all.sh). Formatting will overwrite
|
||||
files when it runs.
|
@ -0,0 +1,11 @@
|
||||
# Makefile for user application
|
||||
|
||||
# Specify this directory relative to the current application.
|
||||
TOCK_USERLAND_BASE_DIR = ../..
|
||||
|
||||
# Which files to compile.
|
||||
C_SRCS := $(wildcard *.c)
|
||||
|
||||
# Include userland master makefile. Contains rules and flags for actually
|
||||
# building the application.
|
||||
include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk
|
@ -0,0 +1,6 @@
|
||||
Accelerometer -> LEDs
|
||||
=====================
|
||||
|
||||
This app makes the accelerometer's readings visual by assigning each
|
||||
direction (X, Y, Z) to a color. Whichever direction's magnitude of acceleration
|
||||
is greatest gets its color lit.
|
@ -0,0 +1,43 @@
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <led.h>
|
||||
#include <ninedof.h>
|
||||
|
||||
int main(void) {
|
||||
printf("[App] Accelerometer -> LEDs\n");
|
||||
|
||||
while (1) {
|
||||
int x, y, z;
|
||||
ninedof_read_acceleration_sync(&x, &y, &z);
|
||||
|
||||
// abs()
|
||||
if (x < 0) x *= -1;
|
||||
if (y < 0) y *= -1;
|
||||
if (z < 0) z *= -1;
|
||||
|
||||
// Set LEDs based on acceleration.
|
||||
int largest = INT_MIN;
|
||||
if (x > largest) largest = x;
|
||||
if (y > largest) largest = y;
|
||||
if (z > largest) largest = z;
|
||||
|
||||
if (x == largest) {
|
||||
led_on(0);
|
||||
} else {
|
||||
led_off(0);
|
||||
}
|
||||
if (y == largest) {
|
||||
led_on(1);
|
||||
} else {
|
||||
led_off(1);
|
||||
}
|
||||
if (z == largest) {
|
||||
led_on(2);
|
||||
} else {
|
||||
led_off(2);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
# Makefile for user application
|
||||
|
||||
# Specify this directory relative to the current application.
|
||||
TOCK_USERLAND_BASE_DIR = ../..
|
||||
|
||||
# Which files to compile.
|
||||
C_SRCS := $(wildcard *.c)
|
||||
|
||||
# Include userland master makefile. Contains rules and flags for actually
|
||||
# building the application.
|
||||
include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk
|
@ -0,0 +1,6 @@
|
||||
ADC App
|
||||
=========
|
||||
|
||||
The canonical "adc" app for an embedded platform. This app will
|
||||
read all the ADC channels of the board that are registered with the kernel.
|
||||
|
@ -0,0 +1,29 @@
|
||||
#include <adc.h>
|
||||
#include <stdio.h>
|
||||
#include <timer.h>
|
||||
|
||||
int main(void) {
|
||||
// Ask the kernel how many ADC channels are on this board.
|
||||
int num_adc;
|
||||
int err = adc_channel_count(&num_adc);
|
||||
if (err < 0) {
|
||||
printf("No ADC on this board.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
printf("ADC Channels: %d\n", num_adc);
|
||||
|
||||
while (true) {
|
||||
for (int channel = 0; channel < num_adc; channel++) {
|
||||
uint16_t value;
|
||||
err = adc_sample_sync(channel, &value);
|
||||
if (err == RETURNCODE_SUCCESS) {
|
||||
printf("Channel %d: %d\n", channel, value);
|
||||
} else {
|
||||
printf("Channel %d: error(%i) %s \n", channel, err, tock_strrcode(err));
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
delay_ms(1000);
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
# Makefile for user application
|
||||
|
||||
# Specify this directory relative to the current application.
|
||||
TOCK_USERLAND_BASE_DIR = ../..
|
||||
|
||||
# Which files to compile.
|
||||
C_SRCS := $(wildcard *.c)
|
||||
|
||||
# External libraries used
|
||||
EXTERN_LIBS += $(TOCK_USERLAND_BASE_DIR)/libnrfserialization
|
||||
|
||||
# Include userland master makefile. Contains rules and flags for actually
|
||||
# building the application.
|
||||
include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk
|
@ -0,0 +1,21 @@
|
||||
UART over BLE
|
||||
=============
|
||||
|
||||
This app implements the peripheral side of the
|
||||
[Nordic UART service](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v11.0.0%2Fble_sdk_app_nus_eval.html&cp=4_0_4_4_2_2_18).
|
||||
To use:
|
||||
|
||||
1. Program this app on a hardware board (imix or Hail).
|
||||
2. Download the "nRF UART" app on a smartphone.
|
||||
3. Run `tockloader listen`.
|
||||
4. In the smartphone app, connect to the device named "tock-uart".
|
||||
5. Any messages you send will appear in the terminal!
|
||||
|
||||
It should be straightforward to re-purpose this app for easy communication
|
||||
between BLE devices.
|
||||
|
||||
Supported Boards
|
||||
----------------
|
||||
|
||||
- Hail
|
||||
- imix
|
@ -0,0 +1,282 @@
|
||||
/* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved.
|
||||
*
|
||||
* The information contained herein is property of Nordic Semiconductor ASA.
|
||||
* Terms and conditions of usage are described in detail in NORDIC
|
||||
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
|
||||
*
|
||||
* Licensees are granted free, non-transferable use of the information. NO
|
||||
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
|
||||
* the file.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ble_nus.h"
|
||||
#include "ble_srv_common.h"
|
||||
#include "sdk_common.h"
|
||||
|
||||
#define BLE_UUID_NUS_TX_CHARACTERISTIC 0x0002 /**< The UUID of the TX Characteristic. */
|
||||
#define BLE_UUID_NUS_RX_CHARACTERISTIC 0x0003 /**< The UUID of the RX Characteristic. */
|
||||
|
||||
#define BLE_NUS_MAX_RX_CHAR_LEN BLE_NUS_MAX_DATA_LEN /**< Maximum length of the RX Characteristic (in bytes). */
|
||||
#define BLE_NUS_MAX_TX_CHAR_LEN BLE_NUS_MAX_DATA_LEN /**< Maximum length of the TX Characteristic (in bytes). */
|
||||
|
||||
#define NUS_BASE_UUID {{0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x00, \
|
||||
0x00, 0x40, 0x6E}} /**< Used vendor specific UUID. */
|
||||
|
||||
/**@brief Function for handling the @ref BLE_GAP_EVT_CONNECTED event from the S110 SoftDevice.
|
||||
*
|
||||
* @param[in] p_nus Nordic UART Service structure.
|
||||
* @param[in] p_ble_evt Pointer to the event received from BLE stack.
|
||||
*/
|
||||
static void on_connect(ble_nus_t * p_nus, ble_evt_t * p_ble_evt)
|
||||
{
|
||||
p_nus->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling the @ref BLE_GAP_EVT_DISCONNECTED event from the S110 SoftDevice.
|
||||
*
|
||||
* @param[in] p_nus Nordic UART Service structure.
|
||||
* @param[in] p_ble_evt Pointer to the event received from BLE stack.
|
||||
*/
|
||||
static void on_disconnect(ble_nus_t * p_nus, ble_evt_t * p_ble_evt)
|
||||
{
|
||||
UNUSED_PARAMETER(p_ble_evt);
|
||||
p_nus->conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling the @ref BLE_GATTS_EVT_WRITE event from the S110 SoftDevice.
|
||||
*
|
||||
* @param[in] p_nus Nordic UART Service structure.
|
||||
* @param[in] p_ble_evt Pointer to the event received from BLE stack.
|
||||
*/
|
||||
static void on_write(ble_nus_t * p_nus, ble_evt_t * p_ble_evt)
|
||||
{
|
||||
ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
|
||||
|
||||
if (
|
||||
(p_evt_write->handle == p_nus->rx_handles.cccd_handle)
|
||||
&&
|
||||
(p_evt_write->len == 2)) {
|
||||
if (ble_srv_is_notification_enabled(p_evt_write->data)) {
|
||||
p_nus->is_notification_enabled = true;
|
||||
} else {
|
||||
p_nus->is_notification_enabled = false;
|
||||
}
|
||||
} else if (
|
||||
(p_evt_write->handle == p_nus->tx_handles.value_handle)
|
||||
&&
|
||||
(p_nus->data_handler != NULL)) {
|
||||
p_nus->data_handler(p_nus, p_evt_write->data, p_evt_write->len);
|
||||
} else {
|
||||
// Do Nothing. This event is not relevant for this service.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for adding RX characteristic.
|
||||
*
|
||||
* @param[in] p_nus Nordic UART Service structure.
|
||||
* @param[in] p_nus_init Information needed to initialize the service.
|
||||
*
|
||||
* @return NRF_SUCCESS on success, otherwise an error code.
|
||||
*/
|
||||
static uint32_t rx_char_add(ble_nus_t * p_nus, const ble_nus_init_t * p_nus_init)
|
||||
{
|
||||
UNUSED_PARAMETER(p_nus_init);
|
||||
/**@snippet [Adding proprietary characteristic to S110 SoftDevice] */
|
||||
ble_gatts_char_md_t char_md;
|
||||
ble_gatts_attr_md_t cccd_md;
|
||||
ble_gatts_attr_t attr_char_value;
|
||||
ble_uuid_t ble_uuid;
|
||||
ble_gatts_attr_md_t attr_md;
|
||||
|
||||
memset(&cccd_md, 0, sizeof(cccd_md));
|
||||
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
|
||||
|
||||
cccd_md.vloc = BLE_GATTS_VLOC_STACK;
|
||||
|
||||
memset(&char_md, 0, sizeof(char_md));
|
||||
|
||||
char_md.char_props.notify = 1;
|
||||
char_md.p_char_user_desc = NULL;
|
||||
char_md.p_char_pf = NULL;
|
||||
char_md.p_user_desc_md = NULL;
|
||||
char_md.p_cccd_md = &cccd_md;
|
||||
char_md.p_sccd_md = NULL;
|
||||
|
||||
ble_uuid.type = p_nus->uuid_type;
|
||||
ble_uuid.uuid = BLE_UUID_NUS_RX_CHARACTERISTIC;
|
||||
|
||||
memset(&attr_md, 0, sizeof(attr_md));
|
||||
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
|
||||
|
||||
attr_md.vloc = BLE_GATTS_VLOC_STACK;
|
||||
attr_md.rd_auth = 0;
|
||||
attr_md.wr_auth = 0;
|
||||
attr_md.vlen = 1;
|
||||
|
||||
memset(&attr_char_value, 0, sizeof(attr_char_value));
|
||||
|
||||
attr_char_value.p_uuid = &ble_uuid;
|
||||
attr_char_value.p_attr_md = &attr_md;
|
||||
attr_char_value.init_len = sizeof(uint8_t);
|
||||
attr_char_value.init_offs = 0;
|
||||
attr_char_value.max_len = BLE_NUS_MAX_RX_CHAR_LEN;
|
||||
|
||||
return sd_ble_gatts_characteristic_add(p_nus->service_handle,
|
||||
&char_md,
|
||||
&attr_char_value,
|
||||
&p_nus->rx_handles);
|
||||
/**@snippet [Adding proprietary characteristic to S110 SoftDevice] */
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for adding TX characteristic.
|
||||
*
|
||||
* @param[in] p_nus Nordic UART Service structure.
|
||||
* @param[in] p_nus_init Information needed to initialize the service.
|
||||
*
|
||||
* @return NRF_SUCCESS on success, otherwise an error code.
|
||||
*/
|
||||
static uint32_t tx_char_add(ble_nus_t * p_nus, const ble_nus_init_t * p_nus_init)
|
||||
{
|
||||
UNUSED_PARAMETER(p_nus_init);
|
||||
ble_gatts_char_md_t char_md;
|
||||
ble_gatts_attr_t attr_char_value;
|
||||
ble_uuid_t ble_uuid;
|
||||
ble_gatts_attr_md_t attr_md;
|
||||
|
||||
memset(&char_md, 0, sizeof(char_md));
|
||||
|
||||
char_md.char_props.write = 1;
|
||||
char_md.char_props.write_wo_resp = 1;
|
||||
char_md.p_char_user_desc = NULL;
|
||||
char_md.p_char_pf = NULL;
|
||||
char_md.p_user_desc_md = NULL;
|
||||
char_md.p_cccd_md = NULL;
|
||||
char_md.p_sccd_md = NULL;
|
||||
|
||||
ble_uuid.type = p_nus->uuid_type;
|
||||
ble_uuid.uuid = BLE_UUID_NUS_TX_CHARACTERISTIC;
|
||||
|
||||
memset(&attr_md, 0, sizeof(attr_md));
|
||||
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
|
||||
|
||||
attr_md.vloc = BLE_GATTS_VLOC_STACK;
|
||||
attr_md.rd_auth = 0;
|
||||
attr_md.wr_auth = 0;
|
||||
attr_md.vlen = 1;
|
||||
|
||||
memset(&attr_char_value, 0, sizeof(attr_char_value));
|
||||
|
||||
attr_char_value.p_uuid = &ble_uuid;
|
||||
attr_char_value.p_attr_md = &attr_md;
|
||||
attr_char_value.init_len = 1;
|
||||
attr_char_value.init_offs = 0;
|
||||
attr_char_value.max_len = BLE_NUS_MAX_TX_CHAR_LEN;
|
||||
|
||||
return sd_ble_gatts_characteristic_add(p_nus->service_handle,
|
||||
&char_md,
|
||||
&attr_char_value,
|
||||
&p_nus->tx_handles);
|
||||
}
|
||||
|
||||
|
||||
void ble_nus_on_ble_evt(ble_nus_t * p_nus, ble_evt_t * p_ble_evt)
|
||||
{
|
||||
if ((p_nus == NULL) || (p_ble_evt == NULL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (p_ble_evt->header.evt_id) {
|
||||
case BLE_GAP_EVT_CONNECTED:
|
||||
on_connect(p_nus, p_ble_evt);
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVT_DISCONNECTED:
|
||||
on_disconnect(p_nus, p_ble_evt);
|
||||
break;
|
||||
|
||||
case BLE_GATTS_EVT_WRITE:
|
||||
on_write(p_nus, p_ble_evt);
|
||||
break;
|
||||
|
||||
default:
|
||||
// No implementation needed.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t ble_nus_init(ble_nus_t * p_nus, const ble_nus_init_t * p_nus_init)
|
||||
{
|
||||
uint32_t err_code;
|
||||
ble_uuid_t ble_uuid;
|
||||
ble_uuid128_t nus_base_uuid = NUS_BASE_UUID;
|
||||
|
||||
VERIFY_PARAM_NOT_NULL(p_nus);
|
||||
VERIFY_PARAM_NOT_NULL(p_nus_init);
|
||||
|
||||
// Initialize the service structure.
|
||||
p_nus->conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||
p_nus->data_handler = p_nus_init->data_handler;
|
||||
p_nus->is_notification_enabled = false;
|
||||
|
||||
/**@snippet [Adding proprietary Service to S110 SoftDevice] */
|
||||
// Add a custom base UUID.
|
||||
err_code = sd_ble_uuid_vs_add(&nus_base_uuid, &p_nus->uuid_type);
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
ble_uuid.type = p_nus->uuid_type;
|
||||
ble_uuid.uuid = BLE_UUID_NUS_SERVICE;
|
||||
|
||||
// Add the service.
|
||||
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
|
||||
&ble_uuid,
|
||||
&p_nus->service_handle);
|
||||
/**@snippet [Adding proprietary Service to S110 SoftDevice] */
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
// Add the RX Characteristic.
|
||||
err_code = rx_char_add(p_nus, p_nus_init);
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
// Add the TX Characteristic.
|
||||
err_code = tx_char_add(p_nus, p_nus_init);
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint32_t ble_nus_string_send(ble_nus_t * p_nus, uint8_t * p_string, uint16_t length)
|
||||
{
|
||||
ble_gatts_hvx_params_t hvx_params;
|
||||
|
||||
VERIFY_PARAM_NOT_NULL(p_nus);
|
||||
|
||||
if ((p_nus->conn_handle == BLE_CONN_HANDLE_INVALID) || (!p_nus->is_notification_enabled)) {
|
||||
return NRF_ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (length > BLE_NUS_MAX_DATA_LEN) {
|
||||
return NRF_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
memset(&hvx_params, 0, sizeof(hvx_params));
|
||||
|
||||
hvx_params.handle = p_nus->rx_handles.value_handle;
|
||||
hvx_params.p_data = p_string;
|
||||
hvx_params.p_len = &length;
|
||||
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
|
||||
|
||||
return sd_ble_gatts_hvx(p_nus->conn_handle, &hvx_params);
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved.
|
||||
*
|
||||
* The information contained herein is property of Nordic Semiconductor ASA.
|
||||
* Terms and conditions of usage are described in detail in NORDIC
|
||||
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
|
||||
*
|
||||
* Licensees are granted free, non-transferable use of the information. NO
|
||||
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
|
||||
* the file.
|
||||
*
|
||||
*/
|
||||
|
||||
/**@file
|
||||
*
|
||||
* @defgroup ble_sdk_srv_nus Nordic UART Service
|
||||
* @{
|
||||
* @ingroup ble_sdk_srv
|
||||
* @brief Nordic UART Service implementation.
|
||||
*
|
||||
* @details The Nordic UART Service is a simple GATT-based service with TX and RX characteristics.
|
||||
* Data received from the peer is passed to the application, and the data received
|
||||
* from the application of this service is sent to the peer as Handle Value
|
||||
* Notifications. This module demonstrates how to implement a custom GATT-based
|
||||
* service and characteristics using the SoftDevice. The service
|
||||
* is used by the application to send and receive ASCII text strings to and from the
|
||||
* peer.
|
||||
*
|
||||
* @note The application must propagate SoftDevice events to the Nordic UART Service module
|
||||
* by calling the ble_nus_on_ble_evt() function from the ble_stack_handler callback.
|
||||
*/
|
||||
|
||||
#ifndef BLE_NUS_H__
|
||||
#define BLE_NUS_H__
|
||||
|
||||
#include "ble.h"
|
||||
#include "ble_srv_common.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define BLE_UUID_NUS_SERVICE 0x0001 /**< The UUID of the Nordic UART Service. */
|
||||
#define BLE_NUS_MAX_DATA_LEN (GATT_MTU_SIZE_DEFAULT - 3) /**< Maximum length of data (in bytes) that can be transmitted to the peer by the Nordic UART service module. */
|
||||
|
||||
/* Forward declaration of the ble_nus_t type. */
|
||||
typedef struct ble_nus_s ble_nus_t;
|
||||
|
||||
/**@brief Nordic UART Service event handler type. */
|
||||
typedef void (*ble_nus_data_handler_t) (ble_nus_t * p_nus, uint8_t * p_data, uint16_t length);
|
||||
|
||||
/**@brief Nordic UART Service initialization structure.
|
||||
*
|
||||
* @details This structure contains the initialization information for the service. The application
|
||||
* must fill this structure and pass it to the service using the @ref ble_nus_init
|
||||
* function.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ble_nus_data_handler_t data_handler; /**< Event handler to be called for handling received data. */
|
||||
} ble_nus_init_t;
|
||||
|
||||
/**@brief Nordic UART Service structure.
|
||||
*
|
||||
* @details This structure contains status information related to the service.
|
||||
*/
|
||||
struct ble_nus_s
|
||||
{
|
||||
uint8_t uuid_type; /**< UUID type for Nordic UART Service Base UUID. */
|
||||
uint16_t service_handle; /**< Handle of Nordic UART Service (as provided by the SoftDevice). */
|
||||
ble_gatts_char_handles_t tx_handles; /**< Handles related to the TX characteristic (as provided by the SoftDevice). */
|
||||
ble_gatts_char_handles_t rx_handles; /**< Handles related to the RX characteristic (as provided by the SoftDevice). */
|
||||
uint16_t conn_handle; /**< Handle of the current connection (as provided by the SoftDevice). BLE_CONN_HANDLE_INVALID if not in a connection. */
|
||||
bool is_notification_enabled; /**< Variable to indicate if the peer has enabled notification of the RX characteristic.*/
|
||||
ble_nus_data_handler_t data_handler; /**< Event handler to be called for handling received data. */
|
||||
};
|
||||
|
||||
/**@brief Function for initializing the Nordic UART Service.
|
||||
*
|
||||
* @param[out] p_nus Nordic UART Service structure. This structure must be supplied
|
||||
* by the application. It is initialized by this function and will
|
||||
* later be used to identify this particular service instance.
|
||||
* @param[in] p_nus_init Information needed to initialize the service.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the service was successfully initialized. Otherwise, an error code is returned.
|
||||
* @retval NRF_ERROR_NULL If either of the pointers p_nus or p_nus_init is NULL.
|
||||
*/
|
||||
uint32_t ble_nus_init(ble_nus_t * p_nus, const ble_nus_init_t * p_nus_init);
|
||||
|
||||
/**@brief Function for handling the Nordic UART Service's BLE events.
|
||||
*
|
||||
* @details The Nordic UART Service expects the application to call this function each time an
|
||||
* event is received from the SoftDevice. This function processes the event if it
|
||||
* is relevant and calls the Nordic UART Service event handler of the
|
||||
* application if necessary.
|
||||
*
|
||||
* @param[in] p_nus Nordic UART Service structure.
|
||||
* @param[in] p_ble_evt Event received from the SoftDevice.
|
||||
*/
|
||||
void ble_nus_on_ble_evt(ble_nus_t * p_nus, ble_evt_t * p_ble_evt);
|
||||
|
||||
/**@brief Function for sending a string to the peer.
|
||||
*
|
||||
* @details This function sends the input string as an RX characteristic notification to the
|
||||
* peer.
|
||||
*
|
||||
* @param[in] p_nus Pointer to the Nordic UART Service structure.
|
||||
* @param[in] p_string String to be sent.
|
||||
* @param[in] length Length of the string.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the string was sent successfully. Otherwise, an error code is returned.
|
||||
*/
|
||||
uint32_t ble_nus_string_send(ble_nus_t * p_nus, uint8_t * p_string, uint16_t length);
|
||||
|
||||
#endif // BLE_NUS_H__
|
||||
|
||||
/** @} */
|
@ -0,0 +1,116 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <ble_advdata.h>
|
||||
#include <nordic_common.h>
|
||||
#include <nrf_error.h>
|
||||
|
||||
#include <eddystone.h>
|
||||
#include <simple_adv.h>
|
||||
#include <simple_ble.h>
|
||||
|
||||
#include <nrf51_serialization.h>
|
||||
|
||||
#include <console.h>
|
||||
#include <tock.h>
|
||||
|
||||
#include "ble_nus.h"
|
||||
#include "nrf.h"
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* BLE
|
||||
******************************************************************************/
|
||||
|
||||
uint16_t conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||
|
||||
// Intervals for advertising and connections
|
||||
simple_ble_config_t ble_config = {
|
||||
.platform_id = 0x00, // used as 4th octect in device BLE address
|
||||
.device_id = DEVICE_ID_DEFAULT,
|
||||
.adv_name = "tock-uart",
|
||||
.adv_interval = MSEC_TO_UNITS(500, UNIT_0_625_MS),
|
||||
.min_conn_interval = MSEC_TO_UNITS(1000, UNIT_1_25_MS),
|
||||
.max_conn_interval = MSEC_TO_UNITS(1250, UNIT_1_25_MS)
|
||||
};
|
||||
|
||||
// State for UART library.
|
||||
static ble_nus_t m_nus;
|
||||
|
||||
void ble_address_set (void) {
|
||||
// nop
|
||||
}
|
||||
|
||||
void ble_evt_user_handler (ble_evt_t* p_ble_evt) {
|
||||
ble_gap_conn_params_t conn_params;
|
||||
memset(&conn_params, 0, sizeof(conn_params));
|
||||
conn_params.min_conn_interval = ble_config.min_conn_interval;
|
||||
conn_params.max_conn_interval = ble_config.max_conn_interval;
|
||||
conn_params.slave_latency = SLAVE_LATENCY;
|
||||
conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT;
|
||||
|
||||
switch (p_ble_evt->header.evt_id) {
|
||||
case BLE_GAP_EVT_CONN_PARAM_UPDATE:
|
||||
// just update them right now
|
||||
sd_ble_gap_conn_param_update(0, &conn_params);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This gets called with the serial data from the BLE central.
|
||||
static void nus_data_handler(ble_nus_t* p_nus, uint8_t* p_data, uint16_t length) {
|
||||
UNUSED_PARAMETER(p_nus);
|
||||
|
||||
// In this app, just print it to the console.
|
||||
putnstr((char*) p_data, length);
|
||||
}
|
||||
|
||||
void ble_evt_connected(ble_evt_t* p_ble_evt) {
|
||||
ble_common_evt_t *common = (ble_common_evt_t*) &p_ble_evt->evt;
|
||||
conn_handle = common->conn_handle;
|
||||
|
||||
ble_nus_on_ble_evt(&m_nus, p_ble_evt);
|
||||
}
|
||||
|
||||
void ble_evt_disconnected(ble_evt_t* p_ble_evt) {
|
||||
conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||
|
||||
ble_nus_on_ble_evt(&m_nus, p_ble_evt);
|
||||
}
|
||||
|
||||
// On a write, need to forward that to NUS library.
|
||||
void ble_evt_write(ble_evt_t* p_ble_evt) {
|
||||
ble_nus_on_ble_evt(&m_nus, p_ble_evt);
|
||||
}
|
||||
|
||||
void ble_error (uint32_t error_code) {
|
||||
printf("BLE ERROR: Code = %d\n", (int)error_code);
|
||||
}
|
||||
|
||||
void services_init (void) {
|
||||
uint32_t err_code;
|
||||
ble_nus_init_t nus_init;
|
||||
memset(&nus_init, 0, sizeof(nus_init));
|
||||
nus_init.data_handler = nus_data_handler;
|
||||
err_code = ble_nus_init(&m_nus, &nus_init);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* MAIN
|
||||
******************************************************************************/
|
||||
|
||||
int main (void) {
|
||||
printf("[BLE] UART over BLE\n");
|
||||
|
||||
// Setup BLE
|
||||
conn_handle = simple_ble_init(&ble_config)->conn_handle;
|
||||
|
||||
// Advertise the UART service
|
||||
ble_uuid_t adv_uuid = {0x0001, BLE_UUID_TYPE_VENDOR_BEGIN};
|
||||
simple_adv_service(&adv_uuid);
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
# Makefile for user application
|
||||
|
||||
# Specify this directory relative to the current application.
|
||||
TOCK_USERLAND_BASE_DIR = ../..
|
||||
|
||||
# Which files to compile.
|
||||
C_SRCS := $(wildcard *.c)
|
||||
|
||||
# External libraries used
|
||||
EXTERN_LIBS += $(TOCK_USERLAND_BASE_DIR)/simple-ble
|
||||
|
||||
# Include userland master makefile. Contains rules and flags for actually
|
||||
# building the application.
|
||||
include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk
|
||||
|
||||
# Include simple-ble's Makefile so it's rebuilt automatically
|
||||
include $(TOCK_USERLAND_BASE_DIR)/simple-ble/Makefile
|
@ -0,0 +1,16 @@
|
||||
Bluetooth Low Energy Advertisement App
|
||||
======================================
|
||||
|
||||
An example application that demonstrates how the Bluetooth Low Energy API can be
|
||||
used to advertise data periodically that runs forever.
|
||||
|
||||
The application does the following:
|
||||
* Configures advertisement name to used in the advertisement
|
||||
* Configures is the device shall be detected
|
||||
* Configures the advertisement interval
|
||||
* Configures list of UUIDs (generic service and temperature service)
|
||||
* Configures temperature to a hard-coded value for demonstration purposes.
|
||||
|
||||
Supported Boards
|
||||
-----------------
|
||||
- nRF52-DK
|
@ -0,0 +1,74 @@
|
||||
#include <ble.h>
|
||||
#include <gap.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <tock.h>
|
||||
|
||||
// Sizes in bytes
|
||||
#define DEVICE_NAME_SIZE 6
|
||||
#define UUIDS_SIZE 4
|
||||
#define MANUFACTURER_DATA_SIZE 2
|
||||
#define FAKE_TEMPERATURE_DATA_SIZE 2
|
||||
|
||||
/*******************************************************************************
|
||||
* MAIN
|
||||
******************************************************************************/
|
||||
|
||||
int main(void) {
|
||||
int err;
|
||||
printf("[Tutorial] BLE Advertising\n");
|
||||
|
||||
// declarations of variables to be used in this BLE example application
|
||||
uint16_t advertising_interval_ms = 300;
|
||||
uint8_t device_name[] = "TockOS";
|
||||
uint16_t uuids[] = {0x1800, 0x1809};
|
||||
uint8_t manufacturer_data[] = {0x13, 0x37};
|
||||
uint8_t fake_temperature_data[] = {0x00, 0x00};
|
||||
|
||||
static uint8_t adv_data_buf[ADV_DATA_MAX_SIZE];
|
||||
|
||||
// configure advertisement interval to 300ms
|
||||
// configure LE only and discoverable
|
||||
printf(" - Initializing BLE... %s\n", device_name);
|
||||
AdvData_t adv_data = gap_adv_data_new(adv_data_buf, sizeof(adv_data_buf));
|
||||
|
||||
gap_add_flags(&adv_data, LE_GENERAL_DISCOVERABLE | BREDR_NOT_SUPPORTED);
|
||||
|
||||
// configure device name as TockOS
|
||||
printf(" - Setting the device name... %s\n", device_name);
|
||||
err = gap_add_device_name(&adv_data, device_name, DEVICE_NAME_SIZE);
|
||||
if (err < RETURNCODE_SUCCESS)
|
||||
printf("ble_advertise_name, error: %s\r\n", tock_strrcode(err));
|
||||
|
||||
// configure list of UUIDs */
|
||||
printf(" - Setting the device UUID...\n");
|
||||
err = gap_add_service_uuid16(&adv_data, uuids, UUIDS_SIZE);
|
||||
if (err < RETURNCODE_SUCCESS)
|
||||
printf("ble_advertise_uuid16, error: %s\r\n", tock_strrcode(err));
|
||||
|
||||
// configure manufacturer data
|
||||
printf(" - Setting manufacturer data...\n");
|
||||
err = gap_add_manufacturer_specific_data(&adv_data, manufacturer_data,
|
||||
MANUFACTURER_DATA_SIZE);
|
||||
if (err < RETURNCODE_SUCCESS)
|
||||
printf("ble_advertise_manufacturer_specific_data, error: %s\r\n",
|
||||
tock_strrcode(err));
|
||||
|
||||
// configure service data
|
||||
printf(" - Setting service data...\n");
|
||||
err = gap_add_service_data(&adv_data, uuids[1], fake_temperature_data,
|
||||
FAKE_TEMPERATURE_DATA_SIZE);
|
||||
if (err < RETURNCODE_SUCCESS)
|
||||
printf("ble_advertise_service_data, error: %s\r\n", tock_strrcode(err));
|
||||
|
||||
// start advertising
|
||||
printf(" - Begin advertising! %s\n", device_name);
|
||||
err = ble_start_advertising(ADV_NONCONN_IND, adv_data.buf, adv_data.offset, advertising_interval_ms);
|
||||
if (err < RETURNCODE_SUCCESS)
|
||||
printf("ble_start_advertising, error: %s\r\n", tock_strrcode(err));
|
||||
|
||||
// configuration complete
|
||||
printf("Now advertising every %d ms as '%s'\n", advertising_interval_ms,
|
||||
device_name);
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
# Makefile for user application
|
||||
|
||||
# Specify this directory relative to the current application.
|
||||
TOCK_USERLAND_BASE_DIR = ../..
|
||||
|
||||
# Which files to compile.
|
||||
CXX_SRCS := $(wildcard *.cpp)
|
||||
|
||||
APP_HEAP_SIZE = 4096
|
||||
|
||||
# Include userland master makefile. Contains rules and flags for actually
|
||||
# building the application.
|
||||
include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk
|
@ -0,0 +1,105 @@
|
||||
#include "advertisement.h"
|
||||
|
||||
Advertisement::Advertisement() {
|
||||
std::memset(&header_, 0, HEADER_SIZE);
|
||||
std::memset(&address_, 0, ADDRESS_SIZE);
|
||||
std::memset(&data_, 0, DATA_MAX_SIZE);
|
||||
}
|
||||
|
||||
Advertisement::Advertisement(const unsigned char* buf, int len)
|
||||
{
|
||||
std::memcpy(&header_, &buf[HEADER_START], HEADER_SIZE);
|
||||
std::memcpy(&address_, &buf[ADDRESS_START], ADDRESS_SIZE);
|
||||
unsigned char data_len = len - HEADER_SIZE - ADDRESS_SIZE;
|
||||
if (data_len > DATA_MAX_SIZE) {
|
||||
data_len = DATA_MAX_SIZE;
|
||||
}
|
||||
std::memcpy(&data_, &buf[DATA_START], data_len);
|
||||
}
|
||||
|
||||
bool Advertisement::device_detected(const Advertisement& other) const
|
||||
{
|
||||
return std::memcmp(&address_, &other.address_, ADDRESS_SIZE) == 0;
|
||||
}
|
||||
|
||||
bool Advertisement::operator==(const Advertisement& other) const {
|
||||
return std::memcmp(this, &other, sizeof(Advertisement)) == 0;
|
||||
}
|
||||
|
||||
bool Advertisement::operator!=(const Advertisement& other) const {
|
||||
return std::memcmp(this, &other, sizeof(Advertisement)) != 0;
|
||||
}
|
||||
|
||||
void Advertisement::print() const
|
||||
{
|
||||
printf("PDU Type: %d %s\r\n", pduType(), pduTypeStr());
|
||||
printf("PDU TxAdd: %d\r\n", pduTxAddSet() ? 1 : 0);
|
||||
printf("PDU RxAdd: %d\r\n", pduRxAddSet() ? 1 : 0);
|
||||
printf("PDU Length: %d\r\n", pduLength());
|
||||
printf("Address: %02x %02x %02x %02x %02x %02x\r\n", address_[5],
|
||||
address_[4], address_[3], address_[2], address_[1], address_[0]);
|
||||
printf("Data: ");
|
||||
for (int i = 0; i < pduLength() - ADDRESS_SIZE; i++) {
|
||||
printf("%02x ", data_[i]);
|
||||
}
|
||||
printf("\r\n\r\n");
|
||||
}
|
||||
|
||||
unsigned char Advertisement::pduLength() const
|
||||
{
|
||||
return header_[1] & PDU_LEN_HEADER_MASK;
|
||||
}
|
||||
|
||||
unsigned char Advertisement::pduType() const
|
||||
{
|
||||
return header_[0] & PDU_TYPE_HEADER_MASK;
|
||||
}
|
||||
|
||||
bool Advertisement::pduTxAddSet() const
|
||||
{
|
||||
return header_[0] & PDU_TXADD_HEADER_MASK;
|
||||
}
|
||||
|
||||
bool Advertisement::pduRxAddSet() const
|
||||
{
|
||||
return header_[0] & PDU_RXADD_HEADER_MASK;
|
||||
}
|
||||
|
||||
const char* Advertisement::pduTypeStr() const
|
||||
{
|
||||
switch (pduType()) {
|
||||
case 0:
|
||||
return "ADV_IND";
|
||||
case 1:
|
||||
return "ADV_DIRECT_IND";
|
||||
case 2:
|
||||
return "NON_CONNECT_IND";
|
||||
case 3:
|
||||
return "SCAN_REQ";
|
||||
case 4:
|
||||
return "SCAN_RSP";
|
||||
case 5:
|
||||
return "CONNECT_REQ";
|
||||
case 6:
|
||||
return "ADV_SCAN_IND";
|
||||
default:
|
||||
return "INVALID ADVERTISEMENT TYPE";
|
||||
}
|
||||
}
|
||||
|
||||
bool Advertisement::checkScanResult(const unsigned char* buf, int len)
|
||||
{
|
||||
if (buf == nullptr) {
|
||||
printf("Malformed scan result: Buffer was null!\n");
|
||||
return false;
|
||||
}
|
||||
if (len < ADV_MIN_SIZE) {
|
||||
printf("Malformed scan result: Result too small!\n");
|
||||
return false;
|
||||
}
|
||||
if (len > ADV_MAX_SIZE) {
|
||||
printf("Malformed scan result: Result too large!\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
const unsigned char HEADER_START = 0;
|
||||
const unsigned char HEADER_SIZE = 2;
|
||||
const unsigned char ADDRESS_START = 2;
|
||||
const unsigned char ADDRESS_SIZE = 6;
|
||||
const unsigned char DATA_START = 8;
|
||||
const unsigned char DATA_MAX_SIZE = 31;
|
||||
|
||||
const unsigned char ADV_MIN_SIZE = 8;
|
||||
const unsigned char ADV_MAX_SIZE = HEADER_SIZE + ADDRESS_SIZE + DATA_MAX_SIZE;
|
||||
|
||||
const unsigned char PDU_LEN_HEADER_MASK = 0x3F;
|
||||
const unsigned char PDU_TYPE_HEADER_MASK = 0xF;
|
||||
const unsigned char PDU_TXADD_HEADER_MASK = 0x40;
|
||||
const unsigned char PDU_RXADD_HEADER_MASK = 0x80;
|
||||
|
||||
class Advertisement {
|
||||
private:
|
||||
unsigned char header_[HEADER_SIZE];
|
||||
unsigned char address_[ADDRESS_SIZE];
|
||||
unsigned char data_[DATA_MAX_SIZE];
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
Advertisement(const unsigned char* buf, int len);
|
||||
Advertisement();
|
||||
|
||||
// Methods
|
||||
bool device_detected(const Advertisement& other) const;
|
||||
bool operator==(const Advertisement& other) const;
|
||||
bool operator!=(const Advertisement& other) const;
|
||||
unsigned char pduType() const;
|
||||
const char* pduTypeStr() const;
|
||||
bool pduTxAddSet() const;
|
||||
bool pduRxAddSet() const;
|
||||
unsigned char pduLength() const;
|
||||
void print() const;
|
||||
static bool checkScanResult(const unsigned char* buf, int len);
|
||||
};
|
@ -0,0 +1,49 @@
|
||||
#include "advertisement_list.h"
|
||||
|
||||
AdvertisementList::AdvertisementList()
|
||||
: currentSize_(0)
|
||||
{
|
||||
list_.resize(MAX_SIZE);
|
||||
}
|
||||
|
||||
bool AdvertisementList::tryAdd(const Advertisement& advertisement)
|
||||
{
|
||||
if (currentSize_ < MAX_SIZE && !containsDevice(advertisement)) {
|
||||
list_.push_front(advertisement);
|
||||
currentSize_ += 1;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AdvertisementList::containsDevice(const Advertisement& advertisement) const
|
||||
{
|
||||
for (auto it = list_.begin(); it != list_.end(); ++it) {
|
||||
if (it->device_detected(advertisement)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AdvertisementList::tryUpdateData(const Advertisement& advertisement)
|
||||
{
|
||||
for (auto it = list_.begin(); it != list_.end(); ++it) {
|
||||
if (*it != advertisement) {
|
||||
list_.remove(*it);
|
||||
list_.push_front(advertisement);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AdvertisementList::printList() const
|
||||
{
|
||||
printf("--------------------------LIST-------------------------\r\n\r\n");
|
||||
for (auto it = list_.begin(); it != list_.end(); ++it) {
|
||||
it->print();
|
||||
}
|
||||
printf("--------------------------END---------------------------\r\n\r\n");
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <forward_list>
|
||||
#include "advertisement.h"
|
||||
|
||||
// this is left outside the class because it doesn't work
|
||||
// code-generation bug?!
|
||||
const int MAX_SIZE = 10;
|
||||
|
||||
class AdvertisementList {
|
||||
private:
|
||||
int currentSize_;
|
||||
std::forward_list<Advertisement> list_;
|
||||
bool containsDevice(const Advertisement& advertisement) const;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
AdvertisementList();
|
||||
|
||||
// Methods
|
||||
bool tryAdd(const Advertisement& advertisement);
|
||||
bool tryUpdateData(const Advertisement& advertisement);
|
||||
void printList() const;
|
||||
};
|
@ -0,0 +1,45 @@
|
||||
#include "advertisement.h"
|
||||
#include "advertisement_list.h"
|
||||
#include <ble.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
* BLE Demo Application
|
||||
* Passive scanner for Bluetooth Low Energy advertisements
|
||||
*/
|
||||
|
||||
const int BUF_SIZE = 39;
|
||||
static unsigned char scan[BUF_SIZE];
|
||||
AdvertisementList list;
|
||||
|
||||
static void callback(int result, int len, __attribute__((unused)) int unused2,
|
||||
__attribute__((unused)) void* ud)
|
||||
{
|
||||
if (result == RETURNCODE_SUCCESS) {
|
||||
if (Advertisement::checkScanResult(scan, len)) {
|
||||
Advertisement advertisement(scan, len);
|
||||
|
||||
if (list.tryAdd(advertisement)) {
|
||||
list.printList();
|
||||
}
|
||||
// FIXME: add this to get dynamic behavior i.e, update every time new advertisement is detected
|
||||
// but might it fill the print buffer, use at your own risk
|
||||
// else if (list.tryUpdateData(advertisement)) {
|
||||
// list.printList();
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("[Tutorial] BLE Passive Scanner\r\n");
|
||||
|
||||
// using the pre-configured advertisement interval
|
||||
int err = ble_start_passive_scan(scan, BUF_SIZE, callback);
|
||||
|
||||
if (err < RETURNCODE_SUCCESS) {
|
||||
printf("ble_start_passive_scan, error: %s\r\n", tock_strrcode(static_cast<returncode_t>(err)));
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
# Makefile for user application
|
||||
|
||||
# Specify this directory relative to the current application.
|
||||
TOCK_USERLAND_BASE_DIR = ../..
|
||||
|
||||
# Which files to compile.
|
||||
C_SRCS := $(wildcard *.c)
|
||||
|
||||
# Include userland master makefile. Contains rules and flags for actually
|
||||
# building the application.
|
||||
include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk
|
@ -0,0 +1,8 @@
|
||||
Blink App
|
||||
=========
|
||||
|
||||
The canonical "blink" app for an embedded platform. This app will
|
||||
blink all of the LEDs that are registered with the kernel.
|
||||
|
||||
To learn more, please see the
|
||||
[Blink an LED](../../../doc/tutorials/01_running_blink.md) tutorial.
|
@ -0,0 +1,24 @@
|
||||
#include <led.h>
|
||||
#include <timer.h>
|
||||
|
||||
int main(void) {
|
||||
// Ask the kernel how many LEDs are on this board.
|
||||
int num_leds;
|
||||
int err = led_count(&num_leds);
|
||||
if (err < 0) return err;
|
||||
|
||||
// Blink the LEDs in a binary count pattern and scale
|
||||
// to the number of LEDs on the board.
|
||||
for (int count = 0; ; count++) {
|
||||
for (int i = 0; i < num_leds; i++) {
|
||||
if (count & (1 << i)) {
|
||||
led_on(i);
|
||||
} else {
|
||||
led_off(i);
|
||||
}
|
||||
}
|
||||
|
||||
// This delay uses an underlying timer in the kernel.
|
||||
delay_ms(250);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user