Skip to content

Commit

Permalink
Merge Arduino Zero compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
gioblu committed Apr 7, 2016
2 parents 4ee78cf + 982e718 commit c59fa8f
Show file tree
Hide file tree
Showing 4 changed files with 380 additions and 158 deletions.
79 changes: 58 additions & 21 deletions PJON.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,59 @@ theory of liability, whether in contract, strict liability, or tort (including
negligence or otherwise) arising in any way out of the use of this software, even if
advised of the possibility of such damage. */

/* modified 2016-03-09 by Esben Soeltoft
- Added support for the Arduino Zero.
Able to achieve the following speeds between two Arduino Zero
Absolute com speed: 5215.20B/s
Practical bandwidth: 4346.00B/s
Packets sent: 2173.00
Mistakes (error found with CRC) 0.00
Fail (no answer from receiver) 0
Busy (Channel is busy or affected by interference) 0
Accuracy: 100.00 %
- All comparison typecasted to preserve proper format between platforms. Comparisons
on AVR defaults to 16 bit unless typecasted, while it defaults to 32 bits on SAMD
This can give problems when comparison is made with timer functions such as millis(),
micros(), delay() and delaymicroseconds().
The function micros() and millis() both returns a unsigned long (uint32_t) on AVR and SAMD.
Delay argument is typecasted (unsigned long) or (uint32_t) on AVR, and delaymicroseconds argument
is typecasted (unsigned int) or (uint16_t) on AVR, while on SAMD the delay argument is typecasted
(uint32_t), and delaymicroseconds argument is typecasted (uint32_t).
- #endif belonging to #ifndef PJON_h moved to end of file
- in function receive_byte, the functioncall digitalWriteFast(_input_pin,LOW) has been removed. It
should not be necessary when the _input_pin is pulled low using a pulldown resistor. Having this
second line only slows down.
If the physical pulldown resistor has to be avoided, pinModeFast(_input_pin,INPUT_PULLDOWN) will
be the correct function call to use, as the INPUT_PULLDOWN is done through pinMode and not
digitalWriteFast.
TO-DO
- Reduce variable size to optimize memory footprint
*/




#include "PJON.h"

/* Initiate PJON passing pin number:
Device's id has to be set through set_id()
before transmitting on the PJON network. */

PJON::PJON(int input_pin) {
PJON::PJON(uint8_t input_pin) {
_input_pin = input_pin;
this->initialize();
}


/* Initiate PJON passing pin number and the device's id: */

PJON::PJON(int input_pin, uint8_t device_id) {
PJON::PJON(uint8_t input_pin, uint8_t device_id) {
_input_pin = input_pin;
_device_id = device_id;
this->initialize();
Expand Down Expand Up @@ -145,7 +183,7 @@ boolean PJON::can_start() {
digitalWriteFast is used instead of standard digitalWrite
function to optimize transmission time */

void PJON::send_bit(uint8_t VALUE, int duration) {
void PJON::send_bit(uint8_t VALUE, uint32_t duration) {
digitalWriteFast(_input_pin, VALUE);
delayMicroseconds(duration);
}
Expand Down Expand Up @@ -202,7 +240,7 @@ Channel analysis Transmission Response
| 0 | | 12 | 4 | 64 | 130 | | 6 |
|_____| |____|________|_________|_____| |_____| */

int PJON::send_string(uint8_t id, char *string, uint8_t length) {
uint16_t PJON::send_string(uint8_t id, char *string, uint8_t length) {
if(!*string) return FAIL;

if(!this->can_start()) return BUSY;
Expand All @@ -225,12 +263,12 @@ int PJON::send_string(uint8_t id, char *string, uint8_t length) {

if(id == BROADCAST) return ACK;

unsigned long time = micros();
int response = FAIL;
uint32_t time = micros();
uint16_t response = FAIL;

/* Receive byte for an initial BIT_SPACER bit + standard bit total duration.
(freak condition used to avoid micros() overflow bug) */
while(response == FAIL && !(micros() - time >= BIT_SPACER + BIT_WIDTH))
while(response == FAIL && !( (uint32_t) ( micros() - time >= BIT_SPACER + BIT_WIDTH )))
response = this->receive_byte();

if(response == ACK || response == NAK) return response;
Expand All @@ -250,7 +288,7 @@ int PJON::send_string(uint8_t id, char *string, uint8_t length) {
| device_id | length | content | state | attempts | timing | registration |
|___________|________|_________|_______|__________|________|______________| */

int PJON::send(uint8_t id, char *packet, uint8_t length, unsigned long timing) {
uint16_t PJON::send(uint8_t id, char *packet, uint8_t length, uint32_t timing) {

if(length >= PACKET_MAX_LENGTH) {
this->_error(CONTENT_TOO_LONG, length);
Expand Down Expand Up @@ -323,7 +361,7 @@ void PJON::update() {

/* Remove a packet from the send list: */

void PJON::remove(int id) {
void PJON::remove(uint16_t id) {
free(packets[id].content);
packets[id].attempts = 0;
packets[id].device_id = NULL;
Expand Down Expand Up @@ -362,21 +400,20 @@ uint8_t PJON::syncronization_bit() {
|
ACCEPTANCE */

int PJON::receive_byte() {
uint16_t PJON::receive_byte() {
/* Initialize the pin and set it to LOW to reduce interference */
pinModeFast(_input_pin, INPUT);
digitalWriteFast(_input_pin, LOW);
unsigned long time = micros();
uint32_t time = micros();
/* Do nothing until the pin stops to be HIGH or passed more time than
BIT_SPACER duration (freak condition used to avoid micros() overflow bug) */
while(digitalReadFast(_input_pin) && !(micros() - time >= BIT_SPACER));
while(digitalReadFast(_input_pin) && !( (uint32_t)( micros() - time >= BIT_SPACER ) ));
/* Save how much time passed */
time = micros() - time;
/* is for sure less than BIT_SPACER, and if is more than ACCEPTANCE
(a minimum HIGH duration) and what is coming after is a LOW bit
probably a byte is coming so try to receive it. */
if(time >= ACCEPTANCE && !this->syncronization_bit())
return (int)this->read_byte();
return (uint8_t)this->read_byte();

return FAIL;
}
Expand All @@ -397,9 +434,9 @@ uint8_t PJON::read_byte() {

/* Try to receive a packet from the pin: */

int PJON::receive() {
int state;
int package_length = PACKET_MAX_LENGTH;
uint16_t PJON::receive() {
uint16_t state;
uint16_t package_length = PACKET_MAX_LENGTH;
uint8_t CRC = 0;

for(uint8_t i = 0; i < package_length; i++) {
Expand Down Expand Up @@ -439,11 +476,11 @@ int PJON::receive() {

/* Try to receive a packet from the pin repeatedly with a maximum duration: */

int PJON::receive(unsigned long duration) {
int response;
long time = micros();
uint16_t PJON::receive(uint32_t duration) {
uint16_t response;
uint32_t time = micros();
/* (freak condition used to avoid micros() overflow bug) */
while(!(micros() - time >= duration)) {
while( !(uint32_t)( micros() - time >= duration ) ) {
response = this->receive();
if(response == ACK)
return ACK;
Expand Down
138 changes: 92 additions & 46 deletions PJON.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,42 @@ theory of liability, whether in contract, strict liability, or tort (including
negligence or otherwise) arising in any way out of the use of this software, even if
advised of the possibility of such damage. */

/* modified 2016-03-09 by Esben Soeltoft
- Added support for the Arduino Zero.
Able to achieve the following speeds between two Arduino Zero
Absolute com speed: 5215.20B/s
Practical bandwidth: 4346.00B/s
Packets sent: 2173.00
Mistakes (error found with CRC) 0.00
Fail (no answer from receiver) 0
Busy (Channel is busy or affected by interference) 0
Accuracy: 100.00 %
- All comparison typecasted to preserve proper format between platforms. Comparisons
on AVR defaults to 16 bit unless typecasted, while it defaults to 32 bits on SAMD
This can give problems when comparison is made with timer functions such as millis(),
micros(), delay() and delaymicroseconds().
The function micros() and millis() both returns a unsigned long (uint32_t) on AVR and SAMD.
Delay argument is typecasted (unsigned long) or (uint32_t) on AVR, and delaymicroseconds argument
is typecasted (unsigned int) or (uint16_t) on AVR, while on SAMD the delay argument is typecasted
(uint32_t), and delaymicroseconds argument is typecasted (uint32_t).
- #endif belonging to #ifndef PJON_h moved to end of file
- in function receive_byte, the functioncall digitalWriteFast(_input_pin,LOW) has been removed. It
should not be necessary when the _input_pin is pulled low using a pulldown resistor. Having this
second line only slows down.
If the physical pulldown resistor has to be avoided, pinModeFast(_input_pin,INPUT_PULLDOWN) will
be the correct function call to use, as the INPUT_PULLDOWN is done through pinMode and not
digitalWriteFast.
TO-DO
- Reduce variable size to optimize memory footprint
*/


#ifndef PJON_h
#define PJON_h
#include "Arduino.h"
Expand All @@ -61,55 +97,63 @@ advised of the possibility of such damage. */
indicated only for networks made by a group of devices with the
same architecture / processor (for example 10 Arduino Uno) */

#define COMPATIBILITY_MODE true
#define COMPATIBILITY_MODE false

/* The following constants setup are quite conservative and determined only
with a huge amount of time and blind testing (without oscilloscope)
tweaking values and analysing results. Theese can be changed to obtain
faster speed. Probably you need experience, time and an oscilloscope. */

#if defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
#if (F_CPU == 16000000 && !COMPATIBILITY_MODE)
#define BIT_WIDTH 20
#define BIT_SPACER 56
#define ACCEPTANCE 20
#define READ_DELAY 8
#endif
#if (F_CPU == 8000000 || COMPATIBILITY_MODE)
#define BIT_WIDTH 40
#define BIT_SPACER 112
#define ACCEPTANCE 40
#define READ_DELAY 4
#endif
#endif

#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#if (F_CPU == 16000000 && !COMPATIBILITY_MODE)
/* Mega has shorter values than Duemianove / Uno because
micros() produces longer delays on ATmega1280/2560 */
#if (COMPATIBILITY_MODE)
#define BIT_WIDTH 40
#define BIT_SPACER 112
#define ACCEPTANCE 40
#define READ_DELAY 4
#else
/* Values known to work
BIT_WIDTH 18
BIT_SPACER 54
ACCEPTANCE 18
READ_DELAY 8
*/
#if defined(ARDUINO_SAMD_ZERO )
#define BIT_WIDTH 14
#define BIT_SPACER 42
#define ACCEPTANCE 14
#define READ_DELAY 4
/* #elif ( (F_CPU == 48000000)|| defined(ARDUINO_ARCH_SAMD) || defined(__SAMD21G18A__) )
#define BIT_WIDTH 18
#define BIT_SPACER 54
#define ACCEPTANCE 18
#define READ_DELAY 8
#endif
#if (F_CPU == 8000000 || COMPATIBILITY_MODE)
/* Mega has shorter values than Duemianove / Uno because
micros() produces longer delays on ATmega1280/2560 */
#define BIT_WIDTH 38
#define BIT_SPACER 110
#define ACCEPTANCE 38
#define READ_DELAY 16
#endif
#endif

#if defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
#if (F_CPU == 8000000 || COMPATIBILITY_MODE) // Internal clock
#define READ_DELAY 8 */
#elif defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
#if ( F_CPU == 16000000 )
/* Mega has shorter values than Duemianove / Uno because
micros() produces longer delays on ATmega1280/2560 */
#define BIT_WIDTH 18
#define BIT_SPACER 54
#define ACCEPTANCE 18
#define READ_DELAY 8
#else // internal clock 8000000
#define BIT_WIDTH 38
#define BIT_SPACER 110
#define ACCEPTANCE 38
#define READ_DELAY 16
#endif
#elif defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
// Internal clock F_CPU == 80000
/* ATtiny has shorter values than Duemianove / Uno because
micros() produces longer delays on ATtiny45/85 */
#define BIT_WIDTH 35
#define BIT_SPACER 107
#define ACCEPTANCE 35
#define READ_DELAY 15
#else
#define BIT_WIDTH 40
#define BIT_SPACER 112
#define ACCEPTANCE 40
#define READ_DELAY 4
#endif
#endif

Expand Down Expand Up @@ -150,9 +194,9 @@ struct packet {
uint8_t device_id;
char *content;
uint8_t length;
unsigned long registration;
int state;
unsigned long timing;
uint32_t registration;
uint16_t state;
uint32_t timing;
};

typedef void (* receiver)(uint8_t length, uint8_t *payload);
Expand All @@ -163,26 +207,27 @@ static void dummy_receiver_handler(uint8_t length, uint8_t *payload) {};
class PJON {

public:
PJON(int input_pin);
PJON(int input_pin, uint8_t id);
PJON(uint8_t input_pin);
PJON(uint8_t input_pin, uint8_t id);

void initialize();

uint8_t get_id();
void set_id(uint8_t id);
void set_receiver(receiver r);
void set_error(error e);

int receive_byte();
int receive();
int receive(unsigned long duration);
uint16_t receive_byte();
uint16_t receive();
uint16_t receive(uint32_t duration);

void send_bit(uint8_t VALUE, int duration);
void send_bit(uint8_t VALUE, uint32_t duration);
void send_byte(uint8_t b);
int send_string(uint8_t id, char *string, uint8_t length);
int send(uint8_t id, char *packet, uint8_t length, unsigned long timing = 0);
uint16_t send_string(uint8_t id, char *string, uint8_t length);
uint16_t send(uint8_t id, char *packet, uint8_t length, uint32_t timing = 0);

void update();
void remove(int id);
void remove(uint16_t id);

uint8_t syncronization_bit();
uint8_t read_byte();
Expand All @@ -193,7 +238,8 @@ class PJON {

private:
uint8_t _device_id;
int _input_pin;
uint8_t _input_pin;
receiver _receiver;
error _error;
};
#endif
Loading

0 comments on commit c59fa8f

Please sign in to comment.