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

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers pjon.cpp Source File

pjon.cpp

00001 
00002  /*-O//\         __     __
00003    |-gfo\       |__| | |  | |\ |
00004    |!y°o:\      |  __| |__| | \| v1.0
00005    |y"s§+`\     Giovanni Blu Mitolo 2012 - 2015
00006   /so+:-..`\    gioscarab@gmail.com
00007   |+/:ngr-*.`\
00008   |5/:%&-a3f.:;\     PJON is a device communications bus system that connects up to 255
00009   \+//u/+g%{osv,,\    arduino boards over one wire up to 5.29kB/s data communication speed.
00010     \=+&/osw+olds.\\   Contains acknowledge, collision detection, CRC and encpryption all done
00011        \:/+-.-°-:+oss\  with micros() and delayMicroseconds(), with no use of interrupts or timers.
00012         | |       \oy\\  Pull down resistor on the bus is generally used to reduce interference.
00013         > <
00014        -| |-
00015 
00016 Copyright (c) 2012-2015, Giovanni Blu Mitolo All rights reserved.
00017 
00018 Redistribution and use in source and binary forms, with or without
00019 modification, are permitted provided that the following conditions are met:
00020 - Redistributions of source code must retain the above copyright
00021    notice, this list of conditions and the following disclaimer.
00022 
00023 -  Redistributions in binary form must reproduce the above copyright
00024    notice, this list of conditions and the following disclaimer in the
00025    documentation and/or other materials provided with the distribution.
00026 
00027 -  All advertising materials mentioning features or use of this software
00028    must display the following acknowledgement:
00029    This product includes PJON software developed by Giovanni Blu Mitolo.
00030 
00031 -  Neither the name of PJON, PJON_ASK nor the
00032    names of its contributors may be used to endorse or promote products
00033    derived from this software without specific prior written permission.
00034 
00035 This software is provided by the copyright holders and contributors"as is"
00036 and any express or implied warranties, including, but not limited to, the
00037 implied warranties of merchantability and fitness for a particular purpose
00038 are disclaimed. In no event shall the copyright holder or contributors be
00039 liable for any direct, indirect, incidental, special, exemplary, or consequential
00040 damages (including, but not limited to, procurement of substitute goods or services;
00041 loss of use, data, or profits; or business interruption) however caused and on any
00042 theory of liability, whether in contract, strict liability, or tort (including
00043 negligence or otherwise) arising in any way out of the use of this software, even if
00044 advised of the possibility of such damage. */
00045 
00046 #include "pjon.h"
00047 
00048 /* Initiate PJON passing pin number:
00049    Device's id has to be set through set_id()
00050    before transmitting on the PJON network.  */
00051 
00052 PJON::PJON(PinName input_pin) : _input_pin(input_pin){
00053   //_input_pin =  DigitalInOut(input_pin);
00054   this->initialize();
00055 }
00056 
00057 
00058 /* Initiate PJON passing pin number and the device's id: */
00059 
00060 PJON::PJON(PinName input_pin, uint8_t device_id) : _input_pin(input_pin) {
00061   _device_id = device_id;
00062   this->initialize();
00063 }
00064 
00065 
00066 /* Initialization tasks: */
00067 
00068 void PJON::initialize() {
00069   this->set_error(dummy_error_handler);
00070   _reg_timer.start();
00071   for(int i = 0; i < MAX_PACKETS; i++) {
00072     packets[i].state = NULL;
00073     packets[i].timing = 0;
00074     packets[i].attempts = 0;
00075   }
00076 }
00077 
00078 
00079 /* Set the device id, passing a single byte (watch out to id collision) */
00080 
00081 void PJON::set_id(uint8_t id) {
00082   _device_id = id;
00083 }
00084 
00085 
00086 /* Pass as a parameter a void function you previously defined in your code.
00087    This will be called when a correct message will be received.
00088    Inside there you can code how to react when data is received.
00089 
00090    void receiver_function(uint8_t length, uint8_t *payload) {
00091     for(int i = 0; i < length; i++)
00092       Serial.print((char)payload[i]);
00093 
00094     Serial.print(" ");
00095     Serial.println(length);
00096   };
00097 
00098   network.set_receiver(receiver_function); */
00099 
00100 void PJON::set_receiver(receiver r) {
00101   _receiver = r;
00102 }
00103 
00104 
00105 /* Pass as a parameter a void function you previously defined in your code.
00106    This will be called when an error in communication occurs
00107 
00108 void error_handler(uint8_t code, uint8_t data) {
00109   Serial.print(code);
00110   Serial.print(" ");
00111   Serial.println(data);
00112 };
00113 
00114 network.set_error(error_handler); */
00115 
00116 void PJON::set_error(pjon_error e) {
00117   _error = e;
00118 }
00119 
00120 
00121 /* Check if the channel is free for transmission:
00122  If an entire byte received contains no 1s it means
00123  that there is no active transmission */
00124 
00125 bool PJON::can_start() {
00126   _input_pin.input(); //pinMode(_input_pin, INPUT);
00127   this->send_bit(0, 2);
00128   if(!this->read_byte())
00129     return true;
00130 
00131   return false;
00132 }
00133 
00134 
00135 /* Send a bit to the pin:
00136  digitalWriteFast is used instead of standard digitalWrite
00137  function to optimize transmission time */
00138 
00139 void PJON::send_bit(uint8_t VALUE, int duration) {
00140   _input_pin = VALUE > 0; //digitalWrite(_input_pin, VALUE);
00141   wait_us(duration);
00142 }
00143 
00144 
00145 /* Every byte is prepended with 2 synchronization padding bits. The first
00146    is a longer than standard logic 1 followed by a standard logic 0.
00147    __________ ___________________________
00148   | SyncPad  | Byte                      |
00149   |______    |___       ___     _____    |
00150   | |    |   |   |     |   |   |     |   |
00151   | | 1  | 0 | 1 | 0 0 | 1 | 0 | 1 1 | 0 |
00152   |_|____|___|___|_____|___|___|_____|___|
00153     |
00154    ACCEPTANCE
00155 
00156 The reception tecnique is based on finding a logic 1 as long as the
00157 first padding bit within a certain threshold, synchronizing to its
00158 falling edge and checking if it is followed by a logic 0. If this
00159 pattern is recognised, reception starts, if not, interference,
00160 synchronization loss or simply absence of communication is
00161 detected at byte level. */
00162 
00163 void PJON::send_byte(uint8_t b) {
00164   _input_pin = 1; //digitalWriteFast(_input_pin, HIGH);
00165   wait_us(BIT_SPACER);
00166   _input_pin = 0; //digitalWriteFast(_input_pin, LOW);
00167   wait_us(BIT_WIDTH);
00168 
00169   for(uint8_t mask = 0x01; mask; mask <<= 1) {
00170     _input_pin = (b & mask) > 0; //digitalWriteFast(_input_pin, b & mask);
00171     wait_us(BIT_WIDTH);
00172   }
00173 }
00174 
00175 
00176 /* An Example of how the string "@" is formatted and sent:
00177 
00178  ID 12            LENGTH 4         CONTENT 64       CRC 130
00179  ________________ ________________ ________________ __________________
00180 |Sync | Byte     |Sync | Byte     |Sync | Byte     |Sync | Byte       |
00181 |___  |     __   |___  |      _   |___  |  _       |___  |  _      _  |
00182 |   | |    |  |  |   | |     | |  |   | | | |      |   | | | |    | | |
00183 | 1 |0|0000|11|00| 1 |0|00000|1|00| 1 |0|0|1|000000| 1 |0|0|1|0000|1|0|
00184 |___|_|____|__|__|___|_|_____|_|__|___|_|_|_|______|___|_|_|_|____|_|_|
00185 
00186 A standard packet transmission is a bidirectional communication between
00187 two devices that can be divided in 3 different phases:
00188 
00189 Channel analysis   Transmission                            Response
00190     _____           _____________________________           _____
00191    | C-A |         | ID | LENGTH | CONTENT | CRC |         | ACK |
00192 <--|-----|---------|----|--------|---------|-----|--> <----|-----|
00193    |  0  |         | 12 |   4    |   64    | 130 |         |  6  |
00194    |_____|         |____|________|_________|_____|         |_____|  */
00195 
00196 int PJON::send_string(uint8_t id, char *string, uint8_t length) {
00197   if (!*string) return FAIL;
00198 
00199   if(!this->can_start()) return BUSY;
00200 
00201   uint8_t CRC = 0;
00202   _input_pin.output(); //pinModeFast(_input_pin, OUTPUT);
00203 
00204   this->send_byte(id);
00205   CRC ^= id;
00206   this->send_byte(length + 3);
00207   CRC ^= length + 3;
00208 
00209   for(uint8_t i = 0; i < length; i++) {
00210     this->send_byte(string[i]);
00211     CRC ^= string[i];
00212   }
00213 
00214   this->send_byte(CRC);
00215   _input_pin = 0; //digitalWriteFast(_input_pin, LOW);
00216 
00217   if(id == BROADCAST) return ACK;
00218   Timer t;
00219   t.start();
00220   //unsigned long time = micros();
00221   int response = FAIL;
00222 
00223   /* Receive byte for an initial BIT_SPACER bit + standard bit total duration.
00224      (freak condition used to avoid micros() overflow bug) */
00225      int time = 0;
00226   while(response == FAIL && !(t.read_us() >= BIT_SPACER + BIT_WIDTH)){
00227     response = this->receive_byte();
00228 if(t.read_us() >= 101 && t.read_us() <= time){
00229                 DigitalOut myled2(LED4);
00230     myled2 = 1;
00231 }
00232 time = t.read_us();
00233     }
00234 
00235   t.stop();
00236   if (response == ACK || response == NAK) {
00237       DigitalOut myled(LED2);
00238     myled = 1;
00239       return response;
00240     }
00241 
00242   return FAIL;
00243 };
00244 
00245 
00246 /* Insert a packet in the send list:
00247  The added packet will be sent in the next update() call.
00248  Using the timing parameter you can set the delay between every
00249  transmission cyclically sending the packet (use remove() function stop it)
00250 
00251  int hi = network.send(99, "HI!", 3, 1000000); // Send hi every second
00252    _________________________________________________________________________
00253   |           |        |         |       |          |        |              |
00254   | device_id | length | content | state | attempts | timing | registration |
00255   |___________|________|_________|_______|__________|________|______________| */
00256 
00257 int PJON::send(uint8_t id, char *packet, uint8_t length, unsigned long timing) {
00258 
00259   char *str = (char *) malloc(length);
00260 
00261   if(str == NULL) {
00262     this->_error(MEMORY_FULL, FAIL);
00263     return FAIL;
00264   }
00265 
00266   memcpy(str, packet, length);
00267 
00268   for(uint8_t i = 0; i < MAX_PACKETS; i++)
00269     if(packets[i].state == NULL) {
00270       packets[i].content = str;
00271       packets[i].device_id = id;
00272       packets[i].length = length;
00273       packets[i].state = TO_BE_SENT;
00274       if(timing > 0) {
00275         packets[i].registration = _reg_timer.read_us();
00276         packets[i].timing = timing;
00277       }
00278       return i;
00279     }
00280 
00281   this->_error(PACKETS_BUFFER_FULL, MAX_PACKETS);
00282   return FAIL;
00283 }
00284 
00285 
00286 /* Update the state of the send list:
00287    check if there are packets to be sent or to be erased
00288    if correctly delivered */
00289 
00290 void PJON::update() {
00291   for(uint8_t i = 0; i < MAX_PACKETS; i++) {
00292     if(packets[i].state != NULL)
00293       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
00294         packets[i].state = send_string(packets[i].device_id, packets[i].content, packets[i].length);
00295 
00296     if(packets[i].state == ACK) {
00297       if(!packets[i].timing)
00298         this->remove(i);
00299       else {
00300         packets[i].attempts = 0;
00301         packets[i].registration = _reg_timer.read_us();
00302         packets[i].state = TO_BE_SENT;
00303       }
00304     }
00305     if(packets[i].state == FAIL) {
00306       packets[i].attempts++;
00307 
00308       if(packets[i].attempts > MAX_ATTEMPTS) {
00309         this->_error(CONNECTION_LOST, packets[i].device_id);
00310         if(!packets[i].timing)
00311           this->remove(i);
00312         else {
00313           packets[i].attempts = 0;
00314           packets[i].registration = _reg_timer.read_us();
00315           packets[i].state = TO_BE_SENT;
00316         }
00317       }
00318     }
00319   }
00320 }
00321 
00322 
00323 /* Remove a packet from the send list: */
00324 
00325 void PJON::remove(int id) {
00326   free(packets[id].content);
00327   packets[id].attempts = 0;
00328   packets[id].device_id = NULL;
00329   packets[id].length = NULL;
00330   packets[id].state = NULL;
00331   packets[id].registration = NULL;
00332 }
00333 
00334 
00335 /* Syncronize with transmitter:
00336  This function is used only in byte syncronization.
00337  READ_DELAY has to be tuned to correctly send and
00338  receive transmissions because this variable shifts
00339  in which portion of the bit, the reading will be
00340  executed by the next read_byte function */
00341 
00342 uint8_t PJON::syncronization_bit() {
00343   wait_us((BIT_WIDTH / 2) - READ_DELAY);
00344   uint8_t bit_value = _input_pin; //digitalReadFast(_input_pin);
00345   wait_us(BIT_WIDTH / 2);
00346   return bit_value;
00347 }
00348 
00349 
00350 /* Check if a byte is coming from the pin:
00351  This function is looking for padding bits before a byte.
00352  If value is 1 for more than ACCEPTANCE and after
00353  that comes a 0 probably a byte is coming:
00354   ________
00355  |  Init  |
00356  |--------|
00357  |_____   |
00358  |  |  |  |
00359  |1 |  |0 |
00360  |__|__|__|
00361     |
00362   ACCEPTANCE */
00363 
00364 int PJON::receive_byte() {
00365   Timer t;
00366   /* Initialize the pin and set it to LOW to reduce interference */
00367   _input_pin.input(); //pinModeFast(_input_pin, INPUT);
00368   _input_pin = 0; //digitalWriteFast(_input_pin, LOW);
00369   //unsigned long time = micros();
00370   t.start();
00371   int time=0;
00372   /* Do nothing until the pin stops to be HIGH or passed more time than
00373      BIT_SPACER duration (freak condition used to avoid micros() overflow bug) */ //MBED needed?
00374   while (_input_pin && !(t.read_us() >= BIT_SPACER));
00375 
00376   /* Save how much time passed */
00377   time = t.read_us();
00378   /* is for sure less than BIT_SPACER, and if is more than ACCEPTANCE
00379      (a minimum HIGH duration) and what is coming after is a LOW bit
00380      probably a byte is coming so try to receive it. */
00381   t.stop();
00382   if(time >= ACCEPTANCE && !this->syncronization_bit()){
00383     DigitalOut myled(LED3);
00384     myled = 1;
00385     return (int)this->read_byte();
00386   }
00387 
00388   return FAIL;
00389 }
00390 
00391 
00392 /* Read a byte from the pin */
00393 
00394 uint8_t PJON::read_byte() {
00395   uint8_t byte_value = 0xB00000000;
00396   wait_us(BIT_WIDTH / 2);
00397   for (uint8_t i = 0; i < 8; i++) {
00398     byte_value += _input_pin << i;
00399     wait_us(BIT_WIDTH);
00400   }
00401   return byte_value;
00402 }
00403 
00404 
00405 /* Try to receive a packet from the pin: */
00406 
00407 int PJON::receive() {
00408   int package_length = PACKET_MAX_LENGTH;
00409   uint8_t CRC = 0;
00410 
00411   for (uint8_t i = 0; i <= package_length; i++) {
00412     data[i] = this->receive_byte();
00413 
00414     if (data[i] == FAIL) return FAIL;
00415 
00416     if(i == 0 && data[i] != _device_id && data[i] != BROADCAST)
00417       return BUSY;
00418 
00419     if(i == 1)
00420       if(data[i] > 0 && data[i] < PACKET_MAX_LENGTH)
00421         package_length = data[i];
00422       else return FAIL;
00423 
00424     CRC ^= data[i];
00425   }
00426 
00427   _input_pin.output(); //pinModeFast(_input_pin, OUTPUT);
00428 
00429   if (!CRC) {
00430     if(data[0] != BROADCAST) {
00431       this->send_byte(ACK);
00432       _input_pin = 0; //digitalWriteFast(_input_pin, LOW);
00433     }
00434     this->_receiver(data[1] - 3, data + 2);
00435     return ACK;
00436   } else {
00437     if(data[0] != BROADCAST) {
00438       this->send_byte(NAK);
00439       _input_pin = 0; //digitalWriteFast(_input_pin, LOW);
00440     }
00441     return NAK;
00442   }
00443 }
00444 
00445 
00446 /* Try to receive a packet from the pin repeatedly with a maximum duration: */
00447 
00448 int PJON::receive(unsigned long duration) {
00449   int response;
00450   Timer t;
00451   t.start();
00452   //long time = micros();
00453   /* (freak condition used to avoid micros() overflow bug) */
00454   while(!(t.read_us() >= duration)) {
00455     response = this->receive();
00456     if(response == ACK){
00457       t.stop();
00458       return ACK;
00459     }
00460   }
00461   return response;
00462 }