Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: SoftSerial SDFileSystem mbed wave_player
MySoftSerial.cpp@29:c2d95d88ee0b, 2017-05-17 (annotated)
- Committer:
- Desertification
- Date:
- Wed May 17 09:43:38 2017 +0000
- Revision:
- 29:c2d95d88ee0b
- Parent:
- 23:121e3235ccfa
DONE (for now)
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| Thomas Cauwelier |
15:75f9bd5d7659 | 1 | // |
| Thomas Cauwelier |
15:75f9bd5d7659 | 2 | // Created by thoma on 10-May-17. |
| Thomas Cauwelier |
15:75f9bd5d7659 | 3 | // |
| Thomas Cauwelier |
15:75f9bd5d7659 | 4 | |
| Thomas Cauwelier |
15:75f9bd5d7659 | 5 | #include "MySoftSerial.h" |
| Thomas Cauwelier |
17:8683abdc1912 | 6 | #include "Manchester.h" |
| Thomas Cauwelier |
15:75f9bd5d7659 | 7 | |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 8 | |
| Thomas Cauwelier |
22:c821bfae25d7 | 9 | MySoftSerial::MySoftSerial(PinName TX, PinName RX, const char *name) { |
| Thomas Cauwelier |
22:c821bfae25d7 | 10 | //MySoftSerial::MySoftSerial(PinName TX, PinName RX, const char *name) : SoftSerial(TX, RX, name) { |
| Thomas Cauwelier |
22:c821bfae25d7 | 11 | tx_en = rx_en = false; |
| Thomas Cauwelier |
22:c821bfae25d7 | 12 | read_buffer = 0; |
| Thomas Cauwelier |
22:c821bfae25d7 | 13 | if (TX != NC) { |
| Thomas Cauwelier |
22:c821bfae25d7 | 14 | tx = new DigitalOut(TX); |
| Thomas Cauwelier |
22:c821bfae25d7 | 15 | tx_en = true; |
| Thomas Cauwelier |
22:c821bfae25d7 | 16 | tx->write(1); |
| Thomas Cauwelier |
22:c821bfae25d7 | 17 | tx_bit = -1; |
| Thomas Cauwelier |
22:c821bfae25d7 | 18 | txticker.attach(this, &MySoftSerial::tx_handler); |
| Thomas Cauwelier |
22:c821bfae25d7 | 19 | } |
| Thomas Cauwelier |
22:c821bfae25d7 | 20 | if (RX != NC) { |
| Thomas Cauwelier |
22:c821bfae25d7 | 21 | rx = new InterruptIn(RX); |
| Thomas Cauwelier |
22:c821bfae25d7 | 22 | rx_en = true; |
| Thomas Cauwelier |
22:c821bfae25d7 | 23 | out_valid = false; |
| Thomas Cauwelier |
22:c821bfae25d7 | 24 | rxticker.attach(this, &MySoftSerial::rx_handler); |
| Thomas Cauwelier |
22:c821bfae25d7 | 25 | rx->fall(this, &MySoftSerial::rx_gpio_irq_handler); |
| Thomas Cauwelier |
22:c821bfae25d7 | 26 | } |
| Thomas Cauwelier |
22:c821bfae25d7 | 27 | |
| Thomas Cauwelier |
22:c821bfae25d7 | 28 | baud(9600); |
| Thomas Cauwelier |
22:c821bfae25d7 | 29 | |
| Desertification | 21:2a91babf5a6d | 30 | max_time_between_transmission_us = 10000; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 31 | calc_required_correction_symbols(); |
| Thomas Cauwelier |
22:c821bfae25d7 | 32 | format(16, MySoftSerial::None, 1); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 33 | start_bits = 4; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 34 | dc_offset_timer.reset(); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 35 | dc_offset_timer.start(); |
| Desertification | 21:2a91babf5a6d | 36 | overhead_us = 200 * 1000000 / SystemCoreClock; |
| Thomas Cauwelier |
22:c821bfae25d7 | 37 | |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 38 | } |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 39 | |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 40 | void MySoftSerial::calc_required_correction_symbols() { |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 41 | required_correction_transmissions = (int) ceil(30000.0 / (16 * bit_period)); |
| Thomas Cauwelier |
15:75f9bd5d7659 | 42 | } |
| Thomas Cauwelier |
15:75f9bd5d7659 | 43 | |
| Thomas Cauwelier |
15:75f9bd5d7659 | 44 | MySoftSerial::~MySoftSerial() { |
| Thomas Cauwelier |
22:c821bfae25d7 | 45 | if (tx_en) |
| Thomas Cauwelier |
22:c821bfae25d7 | 46 | delete(tx); |
| Thomas Cauwelier |
22:c821bfae25d7 | 47 | if (rx_en) |
| Thomas Cauwelier |
22:c821bfae25d7 | 48 | delete(rx); |
| Thomas Cauwelier |
15:75f9bd5d7659 | 49 | } |
| Thomas Cauwelier |
15:75f9bd5d7659 | 50 | |
| Thomas Cauwelier |
15:75f9bd5d7659 | 51 | int MySoftSerial::putc(int c) { |
| Thomas Cauwelier |
17:8683abdc1912 | 52 | c = Manchester::encode(c); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 53 | // check if last transmission was not too long ago |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 54 | dc_offset_timer.stop(); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 55 | if (dc_offset_timer.read_us() > max_time_between_transmission_us) { |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 56 | // transmission was too long ago, must correct the dc offset |
| Thomas Cauwelier |
19:822d93e0bf34 | 57 | correct_dc_offset(); |
| Thomas Cauwelier |
19:822d93e0bf34 | 58 | } |
| Thomas Cauwelier |
15:75f9bd5d7659 | 59 | return _putc(c); |
| Thomas Cauwelier |
15:75f9bd5d7659 | 60 | } |
| Thomas Cauwelier |
15:75f9bd5d7659 | 61 | |
| Desertification | 21:2a91babf5a6d | 62 | int MySoftSerial::_putc(int c) |
| Desertification | 21:2a91babf5a6d | 63 | { |
| Desertification | 21:2a91babf5a6d | 64 | while(!writeable()); |
| Desertification | 21:2a91babf5a6d | 65 | prepare_tx(c); |
| Desertification | 21:2a91babf5a6d | 66 | tx_bit = 0; |
| Desertification | 21:2a91babf5a6d | 67 | txticker.prime(); |
| Desertification | 21:2a91babf5a6d | 68 | tx_handler(); |
| Desertification | 21:2a91babf5a6d | 69 | return 0; |
| Desertification | 21:2a91babf5a6d | 70 | } |
| Desertification | 21:2a91babf5a6d | 71 | |
| Thomas Cauwelier |
15:75f9bd5d7659 | 72 | int MySoftSerial::getc() { |
| Thomas Cauwelier |
19:822d93e0bf34 | 73 | int character = _getc(); |
| Thomas Cauwelier |
19:822d93e0bf34 | 74 | return Manchester::decode(character); |
| Thomas Cauwelier |
17:8683abdc1912 | 75 | } |
| Thomas Cauwelier |
17:8683abdc1912 | 76 | |
| Thomas Cauwelier |
17:8683abdc1912 | 77 | void MySoftSerial::prepare_tx(int c) { |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 78 | // _char is transmitted lsb first |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 79 | _char = c << start_bits; // make room for start bits |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 80 | _char |= 0b1010; //add start bits (lsb first) |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 81 | _char |= 0xFFFF << (start_bits + _bits); // pad with stop bits |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 82 | _char &= ~(1<<_total_bits); // zero after last stop bit ~(1<<_total_bits) = ...111101111... |
| Thomas Cauwelier |
15:75f9bd5d7659 | 83 | } |
| Thomas Cauwelier |
17:8683abdc1912 | 84 | |
| Thomas Cauwelier |
17:8683abdc1912 | 85 | void MySoftSerial::baud(int baudrate) { |
| Thomas Cauwelier |
22:c821bfae25d7 | 86 | bit_period = 1000000 / baudrate; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 87 | calc_required_correction_symbols(); |
| Thomas Cauwelier |
17:8683abdc1912 | 88 | } |
| Thomas Cauwelier |
17:8683abdc1912 | 89 | |
| Thomas Cauwelier |
17:8683abdc1912 | 90 | int MySoftSerial::readable() { |
| Thomas Cauwelier |
22:c821bfae25d7 | 91 | return out_valid; |
| Thomas Cauwelier |
17:8683abdc1912 | 92 | } |
| Thomas Cauwelier |
17:8683abdc1912 | 93 | |
| Thomas Cauwelier |
17:8683abdc1912 | 94 | int MySoftSerial::writeable() { |
| Thomas Cauwelier |
22:c821bfae25d7 | 95 | if (!tx_en) |
| Thomas Cauwelier |
22:c821bfae25d7 | 96 | return false; |
| Thomas Cauwelier |
22:c821bfae25d7 | 97 | if (tx_bit == -1) |
| Thomas Cauwelier |
22:c821bfae25d7 | 98 | return true; |
| Thomas Cauwelier |
22:c821bfae25d7 | 99 | return false; |
| Thomas Cauwelier |
17:8683abdc1912 | 100 | } |
| Thomas Cauwelier |
19:822d93e0bf34 | 101 | |
| Thomas Cauwelier |
19:822d93e0bf34 | 102 | void MySoftSerial::correct_dc_offset() { |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 103 | for (int i = 0; i < required_correction_transmissions; ++i) { |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 104 | //16 bit_periods |
| Thomas Cauwelier |
19:822d93e0bf34 | 105 | tx->write(0); |
| Thomas Cauwelier |
19:822d93e0bf34 | 106 | wait_us(bit_period * 12); // send 12 zeros |
| Thomas Cauwelier |
19:822d93e0bf34 | 107 | tx->write(1); |
| Thomas Cauwelier |
19:822d93e0bf34 | 108 | wait_us(bit_period * 4); // send 4 ones |
| Thomas Cauwelier |
19:822d93e0bf34 | 109 | } |
| Thomas Cauwelier |
19:822d93e0bf34 | 110 | } |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 111 | |
| Thomas Cauwelier |
22:c821bfae25d7 | 112 | int MySoftSerial::_getc( void ) { |
| Thomas Cauwelier |
22:c821bfae25d7 | 113 | while(!readable()); |
| Thomas Cauwelier |
22:c821bfae25d7 | 114 | out_valid = false; |
| Thomas Cauwelier |
22:c821bfae25d7 | 115 | return out_buffer; |
| Thomas Cauwelier |
22:c821bfae25d7 | 116 | } |
| Thomas Cauwelier |
22:c821bfae25d7 | 117 | |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 118 | void MySoftSerial::rx_gpio_irq_handler(void) { |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 119 | rxticker.prime(); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 120 | rxticker.setNext(bit_period + (bit_period >> 1) - overhead_us); // jump 1.5 bit periods |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 121 | rx->fall(NULL); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 122 | rxticker.attach(this, &MySoftSerial::rx_detect_start); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 123 | start_bit = 1; // start from second bit |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 124 | rx_bit = 0; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 125 | rx_error = false; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 126 | } |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 127 | |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 128 | void MySoftSerial::rx_detect_start(void){ |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 129 | int val = rx->read(); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 130 | |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 131 | // check start pattern |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 132 | // todo store pattern in array or int |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 133 | bool ok; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 134 | switch(start_bit){ |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 135 | case 0: |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 136 | ok = val==0; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 137 | break; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 138 | case 1: |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 139 | ok = val==1; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 140 | break; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 141 | case 2: |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 142 | ok = val==0; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 143 | break; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 144 | case 3: |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 145 | ok = val==1; |
| Desertification | 21:2a91babf5a6d | 146 | rxticker.attach(this, &MySoftSerial::rx_handler); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 147 | break; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 148 | default: |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 149 | ok = false; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 150 | } |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 151 | |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 152 | if(ok){ |
| Desertification | 21:2a91babf5a6d | 153 | start_bit++; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 154 | rxticker.setNext(bit_period); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 155 | } else { |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 156 | // start pattern was not correct, this is not a data packet |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 157 | rxticker.detach(); |
| Desertification | 21:2a91babf5a6d | 158 | rx->fall(this, &MySoftSerial::rx_gpio_irq_handler); |
| Desertification | 21:2a91babf5a6d | 159 | } |
| Desertification | 21:2a91babf5a6d | 160 | } |
| Desertification | 21:2a91babf5a6d | 161 | |
| Desertification | 21:2a91babf5a6d | 162 | void MySoftSerial::rx_handler(void) { |
| Desertification | 21:2a91babf5a6d | 163 | //Receive data |
| Desertification | 21:2a91babf5a6d | 164 | int val = rx->read(); |
| Desertification | 21:2a91babf5a6d | 165 | |
| Desertification | 21:2a91babf5a6d | 166 | rxticker.setNext(bit_period); |
| Desertification | 21:2a91babf5a6d | 167 | rx_bit++; |
| Desertification | 21:2a91babf5a6d | 168 | |
| Desertification | 21:2a91babf5a6d | 169 | |
| Desertification | 21:2a91babf5a6d | 170 | if (rx_bit <= _bits) { |
| Desertification | 21:2a91babf5a6d | 171 | read_buffer |= val << (rx_bit - 1); |
| Desertification | 21:2a91babf5a6d | 172 | return; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 173 | } |
| Desertification | 21:2a91babf5a6d | 174 | |
| Desertification | 21:2a91babf5a6d | 175 | //Receive parity |
| Desertification | 21:2a91babf5a6d | 176 | bool parity_count; |
| Desertification | 21:2a91babf5a6d | 177 | if (rx_bit == _bits + 1) { |
| Desertification | 21:2a91babf5a6d | 178 | switch (_parity) { |
| Desertification | 21:2a91babf5a6d | 179 | case Forced1: |
| Desertification | 21:2a91babf5a6d | 180 | if (val == 0) |
| Desertification | 21:2a91babf5a6d | 181 | rx_error = true; |
| Desertification | 21:2a91babf5a6d | 182 | return; |
| Desertification | 21:2a91babf5a6d | 183 | case Forced0: |
| Desertification | 21:2a91babf5a6d | 184 | if (val == 1) |
| Desertification | 21:2a91babf5a6d | 185 | rx_error = true; |
| Desertification | 21:2a91babf5a6d | 186 | return; |
| Desertification | 21:2a91babf5a6d | 187 | case Even: |
| Desertification | 21:2a91babf5a6d | 188 | case Odd: |
| Desertification | 21:2a91babf5a6d | 189 | parity_count = val; |
| Desertification | 21:2a91babf5a6d | 190 | for (int i = 0; i<_bits; i++) { |
| Desertification | 21:2a91babf5a6d | 191 | if (((read_buffer >> i) & 0x01) == 1) |
| Desertification | 21:2a91babf5a6d | 192 | parity_count = !parity_count; |
| Desertification | 21:2a91babf5a6d | 193 | } |
| Desertification | 21:2a91babf5a6d | 194 | if ((parity_count) && (_parity == Even)) |
| Desertification | 21:2a91babf5a6d | 195 | rx_error = true; |
| Desertification | 21:2a91babf5a6d | 196 | if ((!parity_count) && (_parity == Odd)) |
| Desertification | 21:2a91babf5a6d | 197 | rx_error = true; |
| Desertification | 21:2a91babf5a6d | 198 | return; |
| Desertification | 21:2a91babf5a6d | 199 | } |
| Desertification | 21:2a91babf5a6d | 200 | } |
| Desertification | 21:2a91babf5a6d | 201 | |
| Desertification | 21:2a91babf5a6d | 202 | //Receive stop |
| Desertification | 21:2a91babf5a6d | 203 | if (rx_bit < _bits + (bool)_parity + _stop_bits) { |
| Desertification | 21:2a91babf5a6d | 204 | if (!val) |
| Desertification | 21:2a91babf5a6d | 205 | rx_error = true; |
| Desertification | 21:2a91babf5a6d | 206 | return; |
| Desertification | 21:2a91babf5a6d | 207 | } |
| Desertification | 21:2a91babf5a6d | 208 | |
| Desertification | 21:2a91babf5a6d | 209 | //The last stop bit |
| Desertification | 21:2a91babf5a6d | 210 | if (!val) |
| Desertification | 21:2a91babf5a6d | 211 | rx_error = true; |
| Desertification | 21:2a91babf5a6d | 212 | |
| Desertification | 21:2a91babf5a6d | 213 | if (!rx_error) { |
| Desertification | 21:2a91babf5a6d | 214 | out_valid = true; |
| Desertification | 21:2a91babf5a6d | 215 | out_buffer = read_buffer; |
| Desertification | 21:2a91babf5a6d | 216 | fpointer[RxIrq].call(); |
| Desertification | 21:2a91babf5a6d | 217 | } |
| Desertification | 21:2a91babf5a6d | 218 | read_buffer = 0; |
| Desertification | 21:2a91babf5a6d | 219 | rxticker.detach(); |
| Desertification | 21:2a91babf5a6d | 220 | rx->fall(this, &MySoftSerial::rx_gpio_irq_handler); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 221 | } |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 222 | |
| Thomas Cauwelier |
22:c821bfae25d7 | 223 | void MySoftSerial::format(int bits, MySoftSerial::Parity parity, int stop_bits) { |
| Thomas Cauwelier |
22:c821bfae25d7 | 224 | _bits = bits; |
| Thomas Cauwelier |
22:c821bfae25d7 | 225 | _parity = parity; |
| Thomas Cauwelier |
22:c821bfae25d7 | 226 | _stop_bits = stop_bits; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 227 | _total_bits = 4 + _bits + _stop_bits; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 228 | } |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 229 | |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 230 | void MySoftSerial::tx_handler(void) { |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 231 | if (tx_bit == _total_bits) { |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 232 | tx_bit = -1; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 233 | // transmission done, start measuring time until next transmission |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 234 | dc_offset_timer.reset(); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 235 | dc_offset_timer.start(); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 236 | |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 237 | fpointer[TxIrq].call(); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 238 | return; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 239 | } |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 240 | |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 241 | //Flip output |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 242 | int cur_out = tx->read(); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 243 | tx->write(!cur_out); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 244 | |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 245 | //Calculate when to do it again |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 246 | int count = bit_period; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 247 | tx_bit++; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 248 | while(((_char >> tx_bit) & 0x01) == !cur_out) { |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 249 | count+=bit_period; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 250 | tx_bit++; |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 251 | } |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 252 | |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 253 | txticker.setNext(count); |
| Thomas Cauwelier |
20:d2a8daeb6eb7 | 254 | } |