Prep LoRa 2

This commit is contained in:
Theo Arends 2024-02-23 16:20:09 +01:00
parent d4980b9957
commit d258b9a758
1573 changed files with 390970 additions and 3 deletions

25
lib/libesp32/RadioLib/.gitignore vendored Normal file
View 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
View 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

View 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"
)

View File

@ -0,0 +1,3 @@
# Code of Conduct
Don't be an a*shole.

View 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.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,91 @@
# RadioLib ![Build Status](https://github.com/jgromes/RadioLib/workflows/CI/badge.svg) [![PlatformIO Registry](https://badges.registry.platformio.org/packages/jgromes/library/RadioLib.svg)](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.

View 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.

View File

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

View File

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

View 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,
};

View 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.
}

View File

@ -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!
}

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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();
}
}

View File

@ -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
}

View File

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

View File

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

View File

@ -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);
*/
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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();
}
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,3 @@
# generated by ESP-IDF
managed_components/
dependencies.lock

View File

@ -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)

View File

@ -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)

View 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(&reg);
if(calFreq == (int32_t) freq) {
memcpy(&bestReg, &reg, sizeof(bestReg));
break;
} else if(calFreq < (int32_t) freq) {
if(RADIOLIB_ABS(freq - calFreq) < RADIOLIB_ABS(freq - bestFreq)) {
bestFreq = calFreq;
memcpy(&bestReg, &reg, 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

View File

@ -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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
build/

View File

@ -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})

View 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

View File

@ -0,0 +1,8 @@
#!/bin/bash
set -e
mkdir -p build
cd build
cmake ..
make
cd ..

View File

@ -0,0 +1,3 @@
#!/bin/bash
rm -rf ./build

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

View File

@ -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})

View File

@ -0,0 +1 @@
build/

View File

@ -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)

View 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

View File

@ -0,0 +1,8 @@
#!/bin/bash
set -e
mkdir -p build
cd build
cmake -G "CodeBlocks - Unix Makefiles" ..
make
cd ..

View File

@ -0,0 +1,3 @@
#!/bin/bash
rm -rf ./build

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

View File

@ -0,0 +1,2 @@
build/
TockApp.tab

View File

@ -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)

View 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
```

View 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

View 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

View File

@ -0,0 +1,2 @@
build/
.vscode/

View 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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,3 @@
prepush:
cd examples; ./format_all.sh

View File

@ -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)

View File

@ -0,0 +1,294 @@
![Build Status](https://github.com/tock/libtock-c/workflows/ci/badge.svg)
[![slack](https://img.shields.io/badge/slack-tockos-informational)][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.

View File

@ -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)

View File

@ -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

View File

@ -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>`).

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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;
}

View File

@ -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

View File

@ -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.

View File

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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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__
/** @} */

View File

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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

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

View File

@ -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");
}

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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

View File

@ -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.

View File

@ -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