This commit is contained in:
Dooho Yi 2020-09-23 21:36:49 +09:00
commit 73d85f0ae7
19 changed files with 1484 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# osx
.DS_Store
*/.DS_Store
# platformio
.travis.yml
.pio

31
examples/blink.cpp Normal file
View file

@ -0,0 +1,31 @@
/*
Blink
Turns on an LED on for one second, then off for one second, repeatedly.
This example code is in the public domain.
*/
#include <Arduino.h>
// Pin 13 has an LED connected on most Arduino boards.
// Pin 11 has the LED on Teensy 2.0
// Pin 6 has the LED on Teensy++ 2.0
// Pin 13 has the LED on Teensy 3.0
// give it a name:
int led = 26;
// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(3, OUTPUT); // col1
digitalWrite(3, LOW);
pinMode(led, OUTPUT); // col1
}
// the loop routine runs over and over again forever:
void loop() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}

82
examples/radio.cpp Normal file
View file

@ -0,0 +1,82 @@
/*
Name: NRF_Radio.ino
Created: 11/30/2018 2:40:19 AM
Author: michael
*/
#include "NRF51_Radio_library.h"
NRF51_Radio MicrobitRadio = NRF51_Radio();
FrameBuffer *myDataSendData;
const int COL1 = 3; // Column #1 control
const int LED = 26; // 'row 1' led
const long interval = 5000;
// define prototypes for any methods that use 'user types' created in .ino code or use cpp/h files
//
void test(NRF51_Radio _lib1);
// methods below here
//
// the setup function runs once when you press reset or power the board
void setup() {
Serial.begin(115200);
Serial.println("Starting the Radio Library");
pinMode(COL1, OUTPUT);
digitalWrite(COL1, LOW);
pinMode(LED, OUTPUT);
myDataSendData = new FrameBuffer();
MicrobitRadio.hello("Test");
MicrobitRadio.setGroup(10);
MicrobitRadio.enable();
}
// the loop function runs over and over again until power down or reset
void loop() {
digitalWrite(LED, HIGH);
static long currentMillis;
FrameBuffer* myData = MicrobitRadio.recv();
if (myData != NULL) {
Serial.print(myData->length);
Serial.print(" ");
Serial.print(myData->version);
Serial.print(" ");
Serial.print(myData->group);
Serial.print(" ");
Serial.print(myData->protocol);
Serial.print(" ");
Serial.print(myData->payload[10]);
Serial.print(" ");
Serial.println(MicrobitRadio.dataReady());
delete myData;
myDataSendData->length = 3;
myDataSendData->group = 10;
myDataSendData->version = 12;
myDataSendData->protocol = 14;
if (millis() - currentMillis >= interval)
{
Serial.println(currentMillis);
MicrobitRadio.send(myDataSendData);
currentMillis = millis();
}
}
delay(100);
digitalWrite(LED, LOW);
delay(100);
}
//This method demonstrates 1) using a library 2) using user types as function params
void test(NRF51_Radio _lib1)
{
MicrobitRadio.hello("Hello Visual Micro");
}

39
include/README Normal file
View file

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

1
lib/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*.zip

View file

@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View file

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

21
lib/NRF51_Radio_library/LICENSE Executable file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Michael Rahr
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,508 @@
/*
The MIT License (MIT)
Copyright (c) 2016 British Broadcasting Corporation.
This software is provided by Lancaster University by arrangement with the BBC.
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.
*/
#include "Arduino.h"
#include "NRF51_Radio_library.h"
/**
* This is a modification from the Landcaster Uni implementation of simple Radio for the NRF51 on the MICROBIT
* Provides a simple broadcast radio abstraction, built upon the raw nrf51822 RADIO module.
*
* The nrf51822 RADIO module supports a number of proprietary modes of operation in addition to the typical BLE usage.
* This class uses one of these modes to enable simple, point to multipoint communication directly between micro:bits.
*
* TODO: The protocols implemented here do not currently perform any significant form of energy management,
* which means that they will consume far more energy than their BLE equivalent. Later versions of the protocol
* should look to address this through energy efficient broadcast techniques / sleep scheduling. In particular, the GLOSSY
* approach to efficient rebroadcast and network synchronisation would likely provide an effective future step.
*
* TODO: Meshing should also be considered - again a GLOSSY approach may be effective here, and highly complementary to
* the master/slave arachitecture of BLE.
*
* TODO: This implementation may only operated whilst the BLE stack is disabled. The nrf51822 provides a timeslot API to allow
* BLE to cohabit with other protocols. Future work to allow this colocation would be benefical, and would also allow for the
* creation of wireless BLE bridges.
*
* NOTE: This API does not contain any form of encryption, authentication or authorisation. Its purpose is solely for use as a
* teaching aid to demonstrate how simple communications operates, and to provide a sandpit through which learning can take place.
* For serious applications, BLE should be considered a substantially more secure alternative.
*/
NRF51_Radio* NRF51_Radio::instance = NULL;
extern "C" void RADIO_IRQHandler(void)
{
//digitalWrite(LED, HIGH);
if (NRF_RADIO->EVENTS_READY)
{
NRF_RADIO->EVENTS_READY = 0;
// Start listening and wait for the END event
NRF_RADIO->TASKS_START = 1;
}
if (NRF_RADIO->EVENTS_END)
{
NRF_RADIO->EVENTS_END = 0;
if (NRF_RADIO->CRCSTATUS == 1)
{
int sample = (int)NRF_RADIO->RSSISAMPLE;
// Associate this packet's rssi value with the data just
// transferred by DMA receive
NRF51_Radio::instance->setRSSI(-sample);
// Now move on to the next buffer, if possible.
// The queued packet will get the rssi value set above.
NRF51_Radio::instance->queueRxBuf();
// Set the new buffer for DMA
NRF_RADIO->PACKETPTR = (uint32_t)NRF51_Radio::instance->getRxBuf();
}
else
{
NRF51_Radio::instance->setRSSI(0);
}
// Start listening and wait for the END event
NRF_RADIO->TASKS_START = 1;
}
//digitalWrite(LED, LOW);
}
NRF51_Radio::NRF51_Radio()
{
this->status = 0;
this->group = MICROBIT_RADIO_DEFAULT_GROUP;
this->queueDepth = 0;
this->rssi = 0;
this->rxQueue = NULL;
this->rxBuf = NULL;
instance = this;
}
void NRF51_Radio::hello(String msg)
{
Serial.println(msg);
}
/**
* Change the output power level of the transmitter to the given value.
*
* @param power a value in the range 0..7, where 0 is the lowest power and 7 is the highest.
*
* @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range.
*/
int NRF51_Radio::setTransmitPower(int power)
{
if (power < 0 || power >= MICROBIT_BLE_POWER_LEVELS)
return MICROBIT_INVALID_PARAMETER;
NRF_RADIO->TXPOWER = (uint32_t)MICROBIT_BLE_POWER_LEVEL[power];
return MICROBIT_OK;
}
/**
* Change the transmission and reception band of the radio to the given channel
*
* @param band a frequency band in the range 0 - 100. Each step is 1MHz wide, based at 2400MHz.
*
* @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range,
* or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
*/
int NRF51_Radio::setFrequencyBand(int band)
{
if (band < MICROBIT_RADIO_LOWER_FREQ_BAND || band > MICROBIT_RADIO_UPPER_FREQ_BAND)
return MICROBIT_INVALID_PARAMETER;
// We need to disable the radio before setting the frequency
NVIC_DisableIRQ(RADIO_IRQn);
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->TASKS_DISABLE = 1;
while (NRF_RADIO->EVENTS_DISABLED == 0);
NRF_RADIO->FREQUENCY = (uint32_t)band;
// Reenable the radio to wait for the next packet
NRF_RADIO->EVENTS_READY = 0;
NRF_RADIO->TASKS_RXEN = 1;
while (NRF_RADIO->EVENTS_READY == 0);
NRF_RADIO->EVENTS_END = 0;
NRF_RADIO->TASKS_START = 1;
NVIC_ClearPendingIRQ(RADIO_IRQn);
NVIC_EnableIRQ(RADIO_IRQn);
return MICROBIT_OK;
}
/**
* Retrieve a pointer to the currently allocated receive buffer. This is the area of memory
* actively being used by the radio hardware to store incoming data.
*
* @return a pointer to the current receive buffer.
*/
FrameBuffer* NRF51_Radio::getRxBuf()
{
return rxBuf;
}
/**
* Attempt to queue a buffer received by the radio hardware, if sufficient space is available.
*
* @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if a replacement receiver buffer
* could not be allocated (either by policy or memory exhaustion).
*/
int NRF51_Radio::queueRxBuf()
{
if (rxBuf == NULL)
return MICROBIT_INVALID_PARAMETER;
if (queueDepth >= MICROBIT_RADIO_MAXIMUM_RX_BUFFERS)
return MICROBIT_NO_RESOURCES;
// Store the received RSSI value in the frame
rxBuf->rssi = getRSSI();
// Ensure that a replacement buffer is available before queuing.
FrameBuffer *newRxBuf = new FrameBuffer();
if (newRxBuf == NULL)
return MICROBIT_NO_RESOURCES;
// We add to the tail of the queue to preserve causal ordering.
rxBuf->next = NULL;
if (rxQueue == NULL)
{
rxQueue = rxBuf;
}
else
{
FrameBuffer *p = rxQueue;
while (p->next != NULL)
p = p->next;
p->next = rxBuf;
}
// Increase our received packet count
queueDepth++;
// Allocate a new buffer for the receiver hardware to use. the old on will be passed on to higher layer protocols/apps.
rxBuf = newRxBuf;
return MICROBIT_OK;
}
/**
* Sets the RSSI for the most recent packet.
* The value is measured in -dbm. The higher the value, the stronger the signal.
* Typical values are in the range -42 to -128.
*
* @param rssi the new rssi value.
*
* @note should only be called from RADIO_IRQHandler...
*/
int NRF51_Radio::setRSSI(int rssi)
{
if (!(status & MICROBIT_RADIO_STATUS_INITIALISED))
return MICROBIT_NOT_SUPPORTED;
this->rssi = rssi;
return MICROBIT_OK;
}
/**
* Retrieves the current RSSI for the most recent packet.
* The return value is measured in -dbm. The higher the value, the stronger the signal.
* Typical values are in the range -42 to -128.
*
* @return the most recent RSSI value or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
*/
int NRF51_Radio::getRSSI()
{
if (!(status & MICROBIT_RADIO_STATUS_INITIALISED))
return MICROBIT_NOT_SUPPORTED;
return this->rssi;
}
/**
* Initialises the radio for use as a multipoint sender/receiver
*
* @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the BLE stack is running.
*/
int NRF51_Radio::enable()
{
// If the device is already initialised, then there's nothing to do.
if (status & MICROBIT_RADIO_STATUS_INITIALISED)
return MICROBIT_OK;
// If this is the first time we've been enable, allocate out receive buffers.
if (rxBuf == NULL)
rxBuf = new FrameBuffer();
if (rxBuf == NULL)
return MICROBIT_NO_RESOURCES;
// Enable the High Frequency clock on the processor. This is a pre-requisite for
// the RADIO module. Without this clock, no communication is possible.
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
NRF_CLOCK->TASKS_HFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
// Bring up the nrf51822 RADIO module in Nordic's proprietary 1MBps packet radio mode.
setTransmitPower(MICROBIT_RADIO_DEFAULT_TX_POWER);
NRF_RADIO->FREQUENCY = MICROBIT_RADIO_DEFAULT_FREQUENCY;
// Configure for 1Mbps throughput.
// This may sound excessive, but running a high data rates reduces the chances of collisions...
NRF_RADIO->MODE = RADIO_MODE_MODE_Nrf_1Mbit;
// Configure the addresses we use for this protocol. We run ANONYMOUSLY at the core.
// A 40 bit addresses is used. The first 32 bits match the ASCII character code for "uBit".
// Statistically, this provides assurance to avoid other similar 2.4GHz protocols that may be in the vicinity.
// We also map the assigned 8-bit GROUP id into the PREFIX field. This allows the RADIO hardware to perform
// address matching for us, and only generate an interrupt when a packet matching our group is received.
NRF_RADIO->BASE0 = MICROBIT_RADIO_BASE_ADDRESS;
// Join the default group. This will configure the remaining byte in the RADIO hardware module.
setGroup(this->group);
// The RADIO hardware module supports the use of multiple addresses, but as we're running anonymously, we only need one.
// Configure the RADIO module to use the default address (address 0) for both send and receive operations.
NRF_RADIO->TXADDRESS = 0;
NRF_RADIO->RXADDRESSES = 1;
// Packet layout configuration. The nrf51822 has a highly capable and flexible RADIO module that, in addition to transmission
// and reception of data, also contains a LENGTH field, two optional additional 1 byte fields (S0 and S1) and a CRC calculation.
// Configure the packet format for a simple 8 bit length field and no additional fields.
NRF_RADIO->PCNF0 = 0x00000008;
NRF_RADIO->PCNF1 = 0x02040000 | MICROBIT_RADIO_MAX_PACKET_SIZE;
// Most communication channels contain some form of checksum - a mathematical calculation taken based on all the data
// in a packet, that is also sent as part of the packet. When received, this calculation can be repeated, and the results
// from the sender and receiver compared. If they are different, then some corruption of the data ahas happened in transit,
// and we know we can't trust it. The nrf51822 RADIO uses a CRC for this - a very effective checksum calculation.
//
// Enable automatic 16bit CRC generation and checking, and configure how the CRC is calculated.
NRF_RADIO->CRCCNF = RADIO_CRCCNF_LEN_Two;
NRF_RADIO->CRCINIT = 0xFFFF;
NRF_RADIO->CRCPOLY = 0x11021;
// Set the start random value of the data whitening algorithm. This can be any non zero number.
NRF_RADIO->DATAWHITEIV = 0x18;
// Set up the RADIO module to read and write from our internal buffer.
NRF_RADIO->PACKETPTR = (uint32_t)rxBuf;
// Configure the hardware to issue an interrupt whenever a task is complete (e.g. send/receive).
NRF_RADIO->INTENSET = 0x00000008;
NVIC_ClearPendingIRQ(RADIO_IRQn);
NVIC_EnableIRQ(RADIO_IRQn);
NRF_RADIO->SHORTS |= RADIO_SHORTS_ADDRESS_RSSISTART_Msk;
// Start listening for the next packet
NRF_RADIO->EVENTS_READY = 0;
NRF_RADIO->TASKS_RXEN = 1;
while (NRF_RADIO->EVENTS_READY == 0);
NRF_RADIO->EVENTS_END = 0;
NRF_RADIO->TASKS_START = 1;
// Done. Record that our RADIO is configured.
status |= MICROBIT_RADIO_STATUS_INITIALISED;
Serial.println(NRF_RADIO->STATE);
return MICROBIT_OK;
}
/**
* Disables the radio for use as a multipoint sender/receiver.
*
* @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the BLE stack is running.
*/
int NRF51_Radio::disable()
{
if (!(status & MICROBIT_RADIO_STATUS_INITIALISED))
return MICROBIT_OK;
// Disable interrupts and STOP any ongoing packet reception.
NVIC_DisableIRQ(RADIO_IRQn);
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->TASKS_DISABLE = 1;
while (NRF_RADIO->EVENTS_DISABLED == 0);
// record that the radio is now disabled
status &= ~MICROBIT_RADIO_STATUS_INITIALISED;
return MICROBIT_OK;
}
/**
* Sets the radio to listen to packets sent with the given group id.
*
* @param group The group to join. A micro:bit can only listen to one group ID at any time.
*
* @return MICROBIT_OK on success, or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
*/
int NRF51_Radio::setGroup(uint8_t group)
{
// Record our group id locally
this->group = group;
// Also append it to the address of this device, to allow the RADIO module to filter for us.
NRF_RADIO->PREFIX0 = (uint32_t)group;
return MICROBIT_OK;
}
/**
* Determines the number of packets ready to be processed.
*
* @return The number of packets in the receive buffer.
*/
int NRF51_Radio::dataReady()
{
return queueDepth;
}
/**
* Retrieves the next packet from the receive buffer.
* If a data packet is available, then it will be returned immediately to
* the caller. This call will also dequeue the buffer.
*
* @return The buffer containing the the packet. If no data is available, NULL is returned.
*
* @note Once recv() has been called, it is the callers responsibility to
* delete the buffer when appropriate.
*/
FrameBuffer* NRF51_Radio::recv()
{
FrameBuffer *p = rxQueue;
if (p)
{
// Protect shared resource from ISR activity
NVIC_DisableIRQ(RADIO_IRQn);
rxQueue = rxQueue->next;
queueDepth--;
// Allow ISR access to shared resource
NVIC_EnableIRQ(RADIO_IRQn);
}
return p;
}
/**
* Transmits the given buffer onto the broadcast radio.
* The call will wait until the transmission of the packet has completed before returning.
*
* @param data The packet contents to transmit.
*
* @return MICROBIT_OK on success, or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
*/
int NRF51_Radio::send(FrameBuffer *buffer)
{
if (buffer == NULL)
return MICROBIT_INVALID_PARAMETER;
if (buffer->length > MICROBIT_RADIO_MAX_PACKET_SIZE + MICROBIT_RADIO_HEADER_SIZE - 1)
return MICROBIT_INVALID_PARAMETER;
// Firstly, disable the Radio interrupt. We want to wait until the trasmission completes.
NVIC_DisableIRQ(RADIO_IRQn);
// Turn off the transceiver.
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->TASKS_DISABLE = 1;
while (NRF_RADIO->EVENTS_DISABLED == 0);
// Configure the radio to send the buffer provided.
NRF_RADIO->PACKETPTR = (uint32_t)buffer;
// Turn on the transmitter, and wait for it to signal that it's ready to use.
NRF_RADIO->EVENTS_READY = 0;
NRF_RADIO->TASKS_TXEN = 1;
while (NRF_RADIO->EVENTS_READY == 0);
// Start transmission and wait for end of packet.
NRF_RADIO->EVENTS_END = 0;
NRF_RADIO->TASKS_START = 1;
while (NRF_RADIO->EVENTS_END == 0);
// Return the radio to using the default receive buffer
NRF_RADIO->PACKETPTR = (uint32_t)rxBuf;
// Turn off the transmitter.
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->TASKS_DISABLE = 1;
while (NRF_RADIO->EVENTS_DISABLED == 0);
// Start listening for the next packet
NRF_RADIO->EVENTS_READY = 0;
NRF_RADIO->TASKS_RXEN = 1;
while (NRF_RADIO->EVENTS_READY == 0);
NRF_RADIO->EVENTS_END = 0;
NRF_RADIO->TASKS_START = 1;
// Re-enable the Radio interrupt.
NVIC_ClearPendingIRQ(RADIO_IRQn);
NVIC_EnableIRQ(RADIO_IRQn);
return MICROBIT_OK;
}

View file

@ -0,0 +1,220 @@
/*
The MIT License (MIT)
Copyright (c) 2016 British Broadcasting Corporation.
This software is provided by Lancaster University by arrangement with the BBC.
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.
This is a modifyed version of the landcaster implementation
*/
#pragma once
#include "Arduino.h"
#include "nrf51.h"
#include "nrf51_bitfields.h"
#define MICROBIT_RADIO_STATUS_INITIALISED 0x0001
// Default configuration values
#define MICROBIT_RADIO_BASE_ADDRESS 0x75626974
#define MICROBIT_RADIO_DEFAULT_GROUP 1
#define MICROBIT_RADIO_DEFAULT_TX_POWER 6
#define MICROBIT_RADIO_MAX_PACKET_SIZE 32
#define MICROBIT_RADIO_HEADER_SIZE 4
#define MICROBIT_RADIO_MAXIMUM_RX_BUFFERS 4
#define MICROBIT_BLE_POWER_LEVELS 8
// Known Protocol Numbers
#define MICROBIT_RADIO_PROTOCOL_DATAGRAM 1 // A simple, single frame datagram. a little like UDP but with smaller packets. :-)
#define MICROBIT_RADIO_PROTOCOL_EVENTBUS 2 // Transparent propogation of events from one micro:bit to another.
#define MICROBIT_BLE_ENABLED 0
// Events
#define MICROBIT_RADIO_EVT_DATAGRAM 1 // Event to signal that a new datagram has been received.
#define MICROBIT_OK 0x01
#define MICROBIT_NOT_SUPPORTED 0xff
#define MICROBIT_INVALID_PARAMETER 0xfe
#define MICROBIT_NO_RESOURCES 0xfd
// Sets the default radio channel
#ifndef MICROBIT_RADIO_DEFAULT_FREQUENCY
#define MICROBIT_RADIO_DEFAULT_FREQUENCY 7
#endif
// Sets the minimum frequency band permissable for the device
#ifndef MICROBIT_RADIO_LOWER_FREQ_BAND
#define MICROBIT_RADIO_LOWER_FREQ_BAND 0
#endif
// Sets the maximum frequency band permissable for the device
#ifndef MICROBIT_RADIO_UPPER_FREQ_BAND
#define MICROBIT_RADIO_UPPER_FREQ_BAND 83
#endif
const int8_t MICROBIT_BLE_POWER_LEVEL[] = { -30, -20, -16, -12, -8, -4, 0, 4 };
extern "C" void RADIO_IRQHandler(void);
struct FrameBuffer
{
uint8_t length; // The length of the remaining bytes in the packet. includes protocol/version/group fields, excluding the length field itself.
uint8_t version; // Protocol version code.
uint8_t group; // ID of the group to which this packet belongs.
uint8_t protocol; // Inner protocol number c.f. those issued by IANA for IP protocols
uint8_t payload[MICROBIT_RADIO_MAX_PACKET_SIZE]; // User / higher layer protocol data
FrameBuffer *next; // Linkage, to allow this and other protocols to queue packets pending processing.
int rssi; // Received signal strength of this frame.
};
class NRF51_Radio
{
uint8_t group; // The radio group to which this micro:bit belongs.
uint8_t queueDepth; // The number of packets in the receiver queue.
int rssi;
FrameBuffer *rxQueue; // A linear list of incoming packets, queued awaiting processing.
FrameBuffer *rxBuf; // A pointer to the buffer being actively used by the RADIO hardware.
public:
static NRF51_Radio *instance; // A singleton reference, used purely by the interrupt service routine.
NRF51_Radio();
void hello(String msg);
/**
* Sets the RSSI for the most recent packet.
* The value is measured in -dbm. The higher the value, the stronger the signal.
* Typical values are in the range -42 to -128.
*
* @param rssi the new rssi value.
*
* @note should only be called from RADIO_IRQHandler...
*/
int setRSSI(int rssi);
/**
* Change the output power level of the transmitter to the given value.
*
* @param power a value in the range 0..7, where 0 is the lowest power and 7 is the highest.
*
* @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range.
*/
int setTransmitPower(int power);
/**
* Change the transmission and reception band of the radio to the given channel
*
* @param band a frequency band in the range 0 - 100. Each step is 1MHz wide, based at 2400MHz.
*
* @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range,
* or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
*/
int setFrequencyBand(int band);
/**
* Retrieve a pointer to the currently allocated receive buffer. This is the area of memory
* actively being used by the radio hardware to store incoming data.
*
* @return a pointer to the current receive buffer.
*/
FrameBuffer * getRxBuf();
/**
* Attempt to queue a buffer received by the radio hardware, if sufficient space is available.
*
* @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if a replacement receiver buffer
* could not be allocated (either by policy or memory exhaustion).
*/
int queueRxBuf();
/**
* Retrieves the current RSSI for the most recent packet.
* The return value is measured in -dbm. The higher the value, the stronger the signal.
* Typical values are in the range -42 to -128.
*
* @return the most recent RSSI value or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
*/
int getRSSI();
/**
* Initialises the radio for use as a multipoint sender/receiver
*
* @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the BLE stack is running.
*/
int enable();
/**
* Disables the radio for use as a multipoint sender/receiver.
*
* @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the BLE stack is running.
*/
int disable();
/**
* Sets the radio to listen to packets sent with the given group id.
*
* @param group The group to join. A micro:bit can only listen to one group ID at any time.
*
* @return MICROBIT_OK on success, or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
*/
int setGroup(uint8_t group);
/**
* Determines the number of packets ready to be processed.
*
* @return The number of packets in the receive buffer.
*/
int dataReady();
/**
* Retrieves the next packet from the receive buffer.
* If a data packet is available, then it will be returned immediately to
* the caller. This call will also dequeue the buffer.
*
* @return The buffer containing the the packet. If no data is available, NULL is returned.
*
* @note Once recv() has been called, it is the callers responsibility to
* delete the buffer when appropriate.
*/
FrameBuffer* recv();
/**
* Transmits the given buffer onto the broadcast radio.
* The call will wait until the transmission of the packet has completed before returning.
*
* @param data The packet contents to transmit.
*
* @return MICROBIT_OK on success, or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
*/
int send(FrameBuffer *buffer);
private:
int status;
};

View file

@ -0,0 +1,96 @@
# NRF51_Radio_library
This library is based on the Driver from Landcaster University microbit-dal Radio implementation.
To use this library you should have the Microbit Board from sanddeepmistry install, you can get that from here
https://sandeepmistry.github.io/arduino-nRF5/package_nRF5_boards_index.json
This library will for now work in 1Mbit simple mode, it is fully function with the runtime also used in the Python and the Java runtime for the microbit.
You have the following function to play with.
* int setTransmitPower(int power); power 0-7
* int setFrequencyBand(int band); band 0-100
* int getRSSI();
* int enable();
* int disable();
* int setGroup(uint8_t group); group 0-255
* int dataReady();
* FrameBuffer* recv();
* int send(FrameBuffer *buffer); frameBuffer struct
```javascript
struct FrameBuffer
{
uint8_t length; // The length of the remaining bytes in the packet. includes protocol/version/group fields, excluding the length field itself.
uint8_t version; // Protocol version code.
uint8_t group; // ID of the group to which this packet belongs.
uint8_t protocol; // Inner protocol number c.f. those issued by IANA for IP protocols
uint8_t payload[MICROBIT_RADIO_MAX_PACKET_SIZE]; // User / higher layer protocol data
FrameBuffer *next; // Linkage, to allow this and other protocols to queue packets pending processing.
int rssi; // Received signal strength of this frame.
};
```
Example of the use can be found in the example library.
But in general you need to do the following.
```
#include "NRF51_Radio_library.h"
NRF51_Radio MicrobitRadio = NRF51_Radio();
```
In the setup
You can just enable the Radio, the it is ready
MicrobitRadio.enable();
And then in the loop.
```
static long currentMillis;
//Check if there is any data in the buffer
FrameBuffer* myData = MicrobitRadio.recv();
if (myData != NULL) {
Serial.print(myData->length);
Serial.print(" ");
Serial.print(myData->version);
Serial.print(" ");
Serial.print(myData->group);
Serial.print(" ");
Serial.print(myData->protocol);
Serial.print(" ");
Serial.print(myData->payload[10]);
Serial.print(" ");
Serial.println(MicrobitRadio.dataReady());
delete myData; //Remember to delete it
//Fill in some data in the datastruct
myDataSendData->length = 3;
myDataSendData->group = 10;
myDataSendData->version = 12;
myDataSendData->protocol = 14;
//send the data each 5 sec
if (millis() - currentMillis >= interval)
{
Serial.println(currentMillis);
MicrobitRadio.send(myDataSendData);
currentMillis = millis();
}
}
delay(10);
digitalWrite(LED, LOW);
delay(10);
}
```

View file

@ -0,0 +1,87 @@
/*
Name: NRF_Radio.ino
Created: 11/30/2018 2:40:19 AM
Author: michael
*/
#include "NRF51_Radio_library.h"
#include <Adafruit_Microbit.h>
NRF51_Radio MicrobitRadio = NRF51_Radio();
FrameBuffer *myDataSendData;
const int COL1 = 3; // Column #1 control
const int LED = 26; // 'row 1' led
const long interval = 5000;
// define prototypes for any methods that use 'user types' created in .ino code or use cpp/h files
//
void test(NRF51_Radio _lib1);
// methods below here
//
// the setup function runs once when you press reset or power the board
void setup() {
Serial.begin(115200);
Serial.println("Starting the Radio Library");
pinMode(COL1, OUTPUT);
digitalWrite(COL1, LOW);
pinMode(LED, OUTPUT);
myDataSendData = new FrameBuffer();
MicrobitRadio.hello("Test");
MicrobitRadio.enable();
}
// the loop function runs over and over again until power down or reset
void loop() {
digitalWrite(LED, HIGH);
static long currentMillis;
FrameBuffer* myData = MicrobitRadio.recv();
if (myData != NULL) {
Serial.print(myData->length);
Serial.print(" ");
Serial.print(myData->version);
Serial.print(" ");
Serial.print(myData->group);
Serial.print(" ");
Serial.print(myData->protocol);
Serial.print(" ");
Serial.print(myData->payload[10]);
Serial.print(" ");
Serial.println(MicrobitRadio.dataReady());
delete myData;
myDataSendData->length = 3;
myDataSendData->group = 10;
myDataSendData->version = 12;
myDataSendData->protocol = 14;
if (millis() - currentMillis >= interval)
{
Serial.println(currentMillis);
MicrobitRadio.send(myDataSendData);
currentMillis = millis();
}
}
delay(100);
digitalWrite(LED, LOW);
delay(100);
}
//This method demonstrates 1) using a library 2) using user types as function params
void test(NRF51_Radio _lib1)
{
MicrobitRadio.hello("Hello Visual Micro");
}

View file

@ -0,0 +1,163 @@
// Visual Micro is in vMicro>General>Tutorial Mode
//
/*
Name: PingPong_example.ino
Created: 10/12/2018 08.35.50
Author: RAHR\michael
*/
// Define User Types below here or use a .h file
//
#include "NRF51_Radio_library.h"
#include <Adafruit_Microbit.h>
Adafruit_Microbit_Matrix microbit; //We only use this library to get easy access to the MATRIX LED, to sad you need to set the soft device to S110, even though we do not use it
//Should be easy to fix, by removing the ble class of the in the library
NRF51_Radio MicrobitRadio = NRF51_Radio(); //Let's get a new instance of the Radio
enum STATENAME {
START,
SEND,
RECV,
RECVACT,
SENDACK,
WAITTOSEND
};
// Define Function Prototypes that use User Types below here or use a .h file
//
FrameBuffer *myDataSendData; //FrameBuffer for sending data to another device
FrameBuffer* myData;
int current_state; //Var to hold the current state
static long currentMillis; //Var to store the time gone since last time
const long interval = 5000; //Wait time before sending
const long start_time = 10000; //Wait time doing startup, we use this time to see if any other device is already running
const long send_interval = 200; //In state send, after start we send out at 1 sec interval, ontil we start receiving something
const long wait_to_send = 100;
bool got_data = false;
// Define Functions below here or use other .ino or cpp files
//
// The setup() function runs once each time the micro-controller starts
void setup()
{
Serial.begin(115200);
Serial.println("Starting the PingPong example using Group 10, frekvens band 50");
microbit.begin();
microbit.show(microbit.HEART);
MicrobitRadio.enable();
MicrobitRadio.setGroup(10);
MicrobitRadio.setFrequencyBand(50);
Serial.println("Radio running");
current_state = STATENAME::START;
myDataSendData = new FrameBuffer();
currentMillis = millis();
}
// Add the main program code into the continuous loop() function
void loop()
{
if ((MicrobitRadio.dataReady() > 0) &&(got_data!=true)) //Check if there are any data ready for us
{
myData = MicrobitRadio.recv(); //If so then let's get it
got_data = true;
}
switch (current_state)
{
case STATENAME::START: {
if (got_data == true)
{
current_state = STATENAME::RECV; //If we got data then we are not the first one, we change state to recv.
got_data == false; //reset the data flag;
}
if (millis() - currentMillis >= start_time) {
current_state = STATENAME::SEND; //We did not find any signal, so now we assume that we are the first one running
currentMillis = millis(); //so we start to send, and then wait for a ack.
Serial.println("State go to SEND");
}
}; break;
case STATENAME::RECV: {
if (got_data == true)
{
if (myData->group == 2) {
Serial.println("Got Data");
current_state = STATENAME::SENDACK; //If we got data then we are not the first one, we change state to recv.
}
delete myData;
got_data = false;
}
}; break;
case STATENAME::SENDACK: {
Serial.println("Send ACK");
microbit.print("A");
MicrobitRadio.send(myDataSendData);
myDataSendData->length = 3;
myDataSendData->group = 1; //(1=ACK 2=SEND)
myDataSendData->version = 10;
myDataSendData->protocol = 10;
MicrobitRadio.send(myDataSendData);
current_state = STATENAME::WAITTOSEND;
currentMillis = millis();
} break;
case STATENAME::RECVACT: {
current_state = STATENAME::RECV;
}; break;
case STATENAME::WAITTOSEND: {
if (millis() - currentMillis >= wait_to_send) {
Serial.println("State go to SEND");
current_state = STATENAME::SEND;
}
}; break;
case STATENAME::SEND: {
if (got_data == true)
{
if (myData->group == 1) {
current_state = STATENAME::RECV; //If we got data then we are not the first one, we change state to recv.
Serial.println("Got ACK");
}
delete myData;
got_data = false; //reset the data flag;
}
if (millis() - currentMillis >= send_interval) {
Serial.println("State = SEND");
current_state = STATENAME::SEND; //We did not find any signal, so now we assume that we are the first one running
currentMillis = millis(); //so we start to send, and then wait for a ack.
microbit.print("S");
myDataSendData->length = 3;
myDataSendData->group = 2; //(1=ACK 2=SEND)
myDataSendData->version = 10;
myDataSendData->protocol = 10;
MicrobitRadio.send(myDataSendData);
}
}; break;
default:
break;
}
}

View file

@ -0,0 +1,30 @@
#######################################
# Syntax Coloring Map For Wire
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
beginTransmission KEYWORD2
endTransmission KEYWORD2
requestFrom KEYWORD2
onReceive KEYWORD2
onRequest KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
Wire KEYWORD2
Wire1 KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View file

@ -0,0 +1,10 @@
name=NRF51_Radio_library
version=0.2.1
author=Michael Rahr <michael.rahr@gmail.com>
maintainer=Michael Rahr <michael.rahr@gmail.com>
sentence=Microbit Radio library for NRF51, based on runtine inplementation from Landcaster University
paragraph=This library is a extracted version of the landcaster Uni Radio driver for the Microbit, There are multible examples on how to use it.
category=Communication
url=https://github.com/tipih/NRF51_Radio_library
architectures=nRF5

46
lib/README Normal file
View file

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

14
platformio.ini Normal file
View file

@ -0,0 +1,14 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:bbcmicrobit]
platform = nordicnrf51
board = bbcmicrobit
framework = arduino

60
src/main.cpp Normal file
View file

@ -0,0 +1,60 @@
#include "NRF51_Radio_library.h"
NRF51_Radio mbr = NRF51_Radio();
FrameBuffer *frame;
const int COL1 = 3; // Column #1 control
const int LED = 26; // 'row 1' led
void setup() {
Serial.begin(115200);
Serial.println("main.");
pinMode(COL1, OUTPUT);
digitalWrite(COL1, LOW);
pinMode(LED, OUTPUT);
frame = new FrameBuffer();
mbr.hello("main.");
mbr.setGroup(10);
mbr.enable();
}
void loop() {
digitalWrite(LED, HIGH);
FrameBuffer* frm = mbr.recv();
if (frm != NULL) {
Serial.print(frm->length);
Serial.print(" ");
Serial.print(frm->version);
Serial.print(" ");
Serial.print(frm->group);
Serial.print(" ");
Serial.print(frm->protocol);
Serial.print(" ");
Serial.print(frm->payload[10]);
Serial.print(" ");
Serial.println(mbr.dataReady());
delete frm;
frame->length = 3;
frame->group = 10;
frame->version = 12;
frame->protocol = 14;
static long cur_millis;
if (millis() - cur_millis >= 5000) {
Serial.println(cur_millis);
mbr.send(frame);
cur_millis = millis();
}
}
delay(100);
digitalWrite(LED, LOW);
delay(100);
}

11
test/README Normal file
View file

@ -0,0 +1,11 @@
This directory is intended for PIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html