Testing getting PJON working on mbed https://github.com/gioblu/PJON

Dependencies:   mbed

Revision:
0:fa2f348efd7e
Child:
1:bd0ee507dd4c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pjon.cpp	Sun Jan 10 07:26:52 2016 +0000
@@ -0,0 +1,448 @@
+
+ /*-O//\         __     __
+   |-gfo\       |__| | |  | |\ |
+   |!y°o:\      |  __| |__| | \| v1.0
+   |y"s§+`\     Giovanni Blu Mitolo 2012 - 2015
+  /so+:-..`\    gioscarab@gmail.com
+  |+/:ngr-*.`\
+  |5/:%&-a3f.:;\     PJON is a device communications bus system that connects up to 255
+  \+//u/+g%{osv,,\    arduino boards over one wire up to 5.29kB/s data communication speed.
+    \=+&/osw+olds.\\   Contains acknowledge, collision detection, CRC and encpryption all done
+       \:/+-.-°-:+oss\  with micros() and delayMicroseconds(), with no use of interrupts or timers.
+        | |       \oy\\  Pull down resistor on the bus is generally used to reduce interference.
+        > <
+       -| |-
+
+Copyright (c) 2012-2015, Giovanni Blu Mitolo All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+- Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+-  Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+-  All advertising materials mentioning features or use of this software
+   must display the following acknowledgement:
+   This product includes PJON software developed by Giovanni Blu Mitolo.
+
+-  Neither the name of PJON, PJON_ASK nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+This software is provided by the copyright holders and contributors"as is"
+and any express or implied warranties, including, but not limited to, the
+implied warranties of merchantability and fitness for a particular purpose
+are disclaimed. In no event shall the copyright holder or contributors be
+liable for any direct, indirect, incidental, special, exemplary, or consequential
+damages (including, but not limited to, procurement of substitute goods or services;
+loss of use, data, or profits; or business interruption) however caused and on any
+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. */
+
+#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(PinName input_pin) : _input_pin(input_pin){
+  //_input_pin =  DigitalInOut(input_pin);
+  this->initialize();
+}
+
+
+/* Initiate PJON passing pin number and the device's id: */
+
+PJON::PJON(PinName input_pin, uint8_t device_id) : _input_pin(input_pin) {
+  _device_id = device_id;
+  this->initialize();
+}
+
+
+/* Initialization tasks: */
+
+void PJON::initialize() {
+  this->set_error(dummy_error_handler);
+  _reg_timer.start();
+  for(int i = 0; i < MAX_PACKETS; i++) {
+    packets[i].state = NULL;
+    packets[i].timing = 0;
+    packets[i].attempts = 0;
+  }
+}
+
+
+/* Set the device id, passing a single byte (watch out to id collision) */
+
+void PJON::set_id(uint8_t id) {
+  _device_id = id;
+}
+
+
+/* Pass as a parameter a void function you previously defined in your code.
+   This will be called when a correct message will be received.
+   Inside there you can code how to react when data is received.
+
+   void receiver_function(uint8_t length, uint8_t *payload) {
+    for(int i = 0; i < length; i++)
+      Serial.print((char)payload[i]);
+
+    Serial.print(" ");
+    Serial.println(length);
+  };
+
+  network.set_receiver(receiver_function); */
+
+void PJON::set_receiver(receiver r) {
+  _receiver = r;
+}
+
+
+/* Pass as a parameter a void function you previously defined in your code.
+   This will be called when an error in communication occurs
+
+void error_handler(uint8_t code, uint8_t data) {
+  Serial.print(code);
+  Serial.print(" ");
+  Serial.println(data);
+};
+
+network.set_error(error_handler); */
+
+void PJON::set_error(pjon_error e) {
+  _error = e;
+}
+
+
+/* Check if the channel is free for transmission:
+ If an entire byte received contains no 1s it means
+ that there is no active transmission */
+
+bool PJON::can_start() {
+  _input_pin.input(); //pinMode(_input_pin, INPUT);
+  this->send_bit(0, 2);
+  if(!this->read_byte())
+    return true;
+
+  return false;
+}
+
+
+/* Send a bit to the pin:
+ digitalWriteFast is used instead of standard digitalWrite
+ function to optimize transmission time */
+
+void PJON::send_bit(uint8_t VALUE, int duration) {
+  _input_pin = VALUE; //digitalWrite(_input_pin, VALUE);
+  wait_us(duration);
+}
+
+
+/* Every byte is prepended with 2 synchronization padding bits. The first
+   is a longer than standard logic 1 followed by a standard logic 0.
+   __________ ___________________________
+  | SyncPad  | Byte                      |
+  |______    |___       ___     _____    |
+  | |    |   |   |     |   |   |     |   |
+  | | 1  | 0 | 1 | 0 0 | 1 | 0 | 1 1 | 0 |
+  |_|____|___|___|_____|___|___|_____|___|
+    |
+   ACCEPTANCE
+
+The reception tecnique is based on finding a logic 1 as long as the
+first padding bit within a certain threshold, synchronizing to its
+falling edge and checking if it is followed by a logic 0. If this
+pattern is recognised, reception starts, if not, interference,
+synchronization loss or simply absence of communication is
+detected at byte level. */
+
+void PJON::send_byte(uint8_t b) {
+  _input_pin = 1; //digitalWriteFast(_input_pin, HIGH);
+  wait_us(BIT_SPACER);
+  _input_pin = 0; //digitalWriteFast(_input_pin, LOW);
+  wait_us(BIT_WIDTH);
+
+  for(uint8_t mask = 0x01; mask; mask <<= 1) {
+    _input_pin = b & mask; //digitalWriteFast(_input_pin, b & mask);
+    wait_us(BIT_WIDTH);
+  }
+}
+
+
+/* An Example of how the string "@" is formatted and sent:
+
+ ID 12            LENGTH 4         CONTENT 64       CRC 130
+ ________________ ________________ ________________ __________________
+|Sync | Byte     |Sync | Byte     |Sync | Byte     |Sync | Byte       |
+|___  |     __   |___  |      _   |___  |  _       |___  |  _      _  |
+|   | |    |  |  |   | |     | |  |   | | | |      |   | | | |    | | |
+| 1 |0|0000|11|00| 1 |0|00000|1|00| 1 |0|0|1|000000| 1 |0|0|1|0000|1|0|
+|___|_|____|__|__|___|_|_____|_|__|___|_|_|_|______|___|_|_|_|____|_|_|
+
+A standard packet transmission is a bidirectional communication between
+two devices that can be divided in 3 different phases:
+
+Channel analysis   Transmission                            Response
+    _____           _____________________________           _____
+   | C-A |         | ID | LENGTH | CONTENT | CRC |         | ACK |
+<--|-----|---------|----|--------|---------|-----|--> <----|-----|
+   |  0  |         | 12 |   4    |   64    | 130 |         |  6  |
+   |_____|         |____|________|_________|_____|         |_____|  */
+
+int PJON::send_string(uint8_t id, char *string, uint8_t length) {
+  if (!*string) return FAIL;
+
+  if(!this->can_start()) return BUSY;
+
+  uint8_t CRC = 0;
+  _input_pin.output(); //pinModeFast(_input_pin, OUTPUT);
+
+  this->send_byte(id);
+  CRC ^= id;
+  this->send_byte(length + 3);
+  CRC ^= length + 3;
+
+  for(uint8_t i = 0; i < length; i++) {
+    this->send_byte(string[i]);
+    CRC ^= string[i];
+  }
+
+  this->send_byte(CRC);
+  _input_pin = 0; //digitalWriteFast(_input_pin, LOW);
+
+  if(id == BROADCAST) return ACK;
+  Timer t;
+  t.start();
+  //unsigned long time = micros();
+  int 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 && !(t.read_us() >= BIT_SPACER + BIT_WIDTH))
+    response = this->receive_byte();
+
+  t.stop();
+  if (response == ACK || response == NAK) return response;
+
+  return FAIL;
+};
+
+
+/* Insert a packet in the send list:
+ The added packet will be sent in the next update() call.
+ Using the timing parameter you can set the delay between every
+ transmission cyclically sending the packet (use remove() function stop it)
+
+ int hi = network.send(99, "HI!", 3, 1000000); // Send hi every second
+   _________________________________________________________________________
+  |           |        |         |       |          |        |              |
+  | device_id | length | content | state | attempts | timing | registration |
+  |___________|________|_________|_______|__________|________|______________| */
+
+int PJON::send(uint8_t id, char *packet, uint8_t length, unsigned long timing) {
+
+  char *str = (char *) malloc(length);
+
+  if(str == NULL) {
+    this->_error(MEMORY_FULL, FAIL);
+    return FAIL;
+  }
+
+  memcpy(str, packet, length);
+
+  for(uint8_t i = 0; i < MAX_PACKETS; i++)
+    if(packets[i].state == NULL) {
+      packets[i].content = str;
+      packets[i].device_id = id;
+      packets[i].length = length;
+      packets[i].state = TO_BE_SENT;
+      if(timing > 0) {
+        packets[i].registration = _reg_timer.read_us();
+        packets[i].timing = timing;
+      }
+      return i;
+    }
+
+  this->_error(PACKETS_BUFFER_FULL, MAX_PACKETS);
+  return FAIL;
+}
+
+
+/* Update the state of the send list:
+   check if there are packets to be sent or to be erased
+   if correctly delivered */
+
+void PJON::update() {
+  for(uint8_t i = 0; i < MAX_PACKETS; i++) {
+    if(packets[i].state != NULL)
+      if(_reg_timer.read_us() - packets[i].registration > packets[i].timing + (packets[i].attempts * packets[i].attempts)) //pow((float)packets[i].attempts, 2)) //avoid float operation
+        packets[i].state = send_string(packets[i].device_id, packets[i].content, packets[i].length);
+
+    if(packets[i].state == ACK) {
+      if(!packets[i].timing)
+        this->remove(i);
+      else {
+        packets[i].attempts = 0;
+        packets[i].registration = _reg_timer.read_us();
+        packets[i].state = TO_BE_SENT;
+      }
+    }
+    if(packets[i].state == FAIL) {
+      packets[i].attempts++;
+
+      if(packets[i].attempts > MAX_ATTEMPTS) {
+        this->_error(CONNECTION_LOST, packets[i].device_id);
+        if(!packets[i].timing)
+          this->remove(i);
+        else {
+          packets[i].attempts = 0;
+          packets[i].registration = _reg_timer.read_us();
+          packets[i].state = TO_BE_SENT;
+        }
+      }
+    }
+  }
+}
+
+
+/* Remove a packet from the send list: */
+
+void PJON::remove(int id) {
+  free(packets[id].content);
+  packets[id].attempts = 0;
+  packets[id].device_id = NULL;
+  packets[id].length = NULL;
+  packets[id].state = NULL;
+  packets[id].registration = NULL;
+}
+
+
+/* Syncronize with transmitter:
+ This function is used only in byte syncronization.
+ READ_DELAY has to be tuned to correctly send and
+ receive transmissions because this variable shifts
+ in which portion of the bit, the reading will be
+ executed by the next read_byte function */
+
+uint8_t PJON::syncronization_bit() {
+  wait_us((BIT_WIDTH / 2) - READ_DELAY);
+  uint8_t bit_value = _input_pin; //digitalReadFast(_input_pin);
+  wait_us(BIT_WIDTH / 2);
+  return bit_value;
+}
+
+
+/* Check if a byte is coming from the pin:
+ This function is looking for padding bits before a byte.
+ If value is 1 for more than ACCEPTANCE and after
+ that comes a 0 probably a byte is coming:
+  ________
+ |  Init  |
+ |--------|
+ |_____   |
+ |  |  |  |
+ |1 |  |0 |
+ |__|__|__|
+    |
+  ACCEPTANCE */
+
+int PJON::receive_byte() {
+  Timer t;
+  /* Initialize the pin and set it to LOW to reduce interference */
+  _input_pin.input(); //pinModeFast(_input_pin, INPUT);
+  _input_pin = 0; //digitalWriteFast(_input_pin, LOW);
+  //unsigned long time = micros();
+  t.start();
+  int time=0;
+  /* 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) */ //MBED needed?
+  while (_input_pin && !(t.read_us() >= BIT_SPACER));
+  /* Save how much time passed */
+  time = t.read_us();
+  /* 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. */
+     t.stop();
+  if(time >= ACCEPTANCE && !this->syncronization_bit()){
+    return (int)this->read_byte();
+  }
+
+  return FAIL;
+}
+
+
+/* Read a byte from the pin */
+
+uint8_t PJON::read_byte() {
+  uint8_t byte_value = 0xB00000000;
+  wait_us(BIT_WIDTH / 2);
+  for (uint8_t i = 0; i < 8; i++) {
+    byte_value += _input_pin << i;
+    wait_us(BIT_WIDTH);
+  }
+  return byte_value;
+}
+
+
+/* Try to receive a packet from the pin: */
+
+int PJON::receive() {
+  int package_length = PACKET_MAX_LENGTH;
+  uint8_t CRC = 0;
+
+  for (uint8_t i = 0; i <= package_length; i++) {
+    data[i] = this->receive_byte();
+
+    if (data[i] == FAIL) return FAIL;
+
+    if(i == 0 && data[i] != _device_id && data[i] != BROADCAST)
+      return BUSY;
+
+    if(i == 1)
+      if(data[i] > 0 && data[i] < PACKET_MAX_LENGTH)
+        package_length = data[i];
+      else return FAIL;
+
+    CRC ^= data[i];
+  }
+
+  _input_pin.output(); //pinModeFast(_input_pin, OUTPUT);
+
+  if (!CRC) {
+    if(data[0] != BROADCAST) {
+      this->send_byte(ACK);
+      _input_pin = 0; //digitalWriteFast(_input_pin, LOW);
+    }
+    this->_receiver(data[1] - 3, data + 2);
+    return ACK;
+  } else {
+    if(data[0] != BROADCAST) {
+      this->send_byte(NAK);
+      _input_pin = 0; //digitalWriteFast(_input_pin, LOW);
+    }
+    return NAK;
+  }
+}
+
+
+/* Try to receive a packet from the pin repeatedly with a maximum duration: */
+
+int PJON::receive(unsigned long duration) {
+  int response;
+  Timer t;
+  t.start();
+  //long time = micros();
+  /* (freak condition used to avoid micros() overflow bug) */
+  while(!(t.read_us() >= duration)) {
+    response = this->receive();
+    if(response == ACK){
+      t.stop();
+      return ACK;
+    }
+  }
+  return response;
+}
\ No newline at end of file