Jordan Earls
/
pjon_testing
Testing getting PJON working on mbed https://github.com/gioblu/PJON
Embed:
(wiki syntax)
Show/hide line numbers
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 }
Generated on Sat Jul 23 2022 04:31:54 by 1.7.2