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: nRF51822
Source/BLE_Stuff.cpp@32:749e1c060d03, 2019-07-30 (annotated)
- Committer:
- sgetz7908
- Date:
- Tue Jul 30 19:57:19 2019 +0000
- Revision:
- 32:749e1c060d03
- Parent:
- 30:76b51e525c40
- Child:
- 34:c122d842ad9a
Changed EOL_MAX_USES to 120.; Fixed issue that when the connect inactivity timeout was exceeded, it would lockup.;
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| sgetz7908 | 23:7ca590427f0e | 1 | |
| sgetz7908 | 23:7ca590427f0e | 2 | /// @file BLE_Stuff.cpp |
| sgetz7908 | 23:7ca590427f0e | 3 | #include <events/mbed_events.h> |
| sgetz7908 | 23:7ca590427f0e | 4 | #include <mbed.h> |
| sgetz7908 | 23:7ca590427f0e | 5 | #include "ble/BLE.h" |
| sgetz7908 | 23:7ca590427f0e | 6 | #include "ble/Gap.h" |
| sgetz7908 | 23:7ca590427f0e | 7 | #include "ble/services/UARTService.h" |
| sgetz7908 | 23:7ca590427f0e | 8 | |
| sgetz7908 | 23:7ca590427f0e | 9 | #include "BLE_Stuff.h" |
| sgetz7908 | 23:7ca590427f0e | 10 | #include "infoService.h" |
| sgetz7908 | 23:7ca590427f0e | 11 | #include "hw.h" |
| sgetz7908 | 23:7ca590427f0e | 12 | #include "log.h" |
| sgetz7908 | 23:7ca590427f0e | 13 | #include "main.h" |
| sgetz7908 | 23:7ca590427f0e | 14 | #include "log.h" |
| sgetz7908 | 23:7ca590427f0e | 15 | #include "nrf_soc.h" |
| sgetz7908 | 23:7ca590427f0e | 16 | |
| sgetz7908 | 23:7ca590427f0e | 17 | extern EventQueue eventQueue; //(/* event count */ 16 * EVENTS_EVENT_SIZE); |
| sgetz7908 | 23:7ca590427f0e | 18 | UARTService *uartServicePtr = NULL; |
| sgetz7908 | 23:7ca590427f0e | 19 | |
| sgetz7908 | 32:749e1c060d03 | 20 | int timeout_id = 0; |
| sgetz7908 | 23:7ca590427f0e | 21 | |
| sgetz7908 | 23:7ca590427f0e | 22 | int adv_timeout_ID = 0; |
| sgetz7908 | 23:7ca590427f0e | 23 | |
| sgetz7908 | 23:7ca590427f0e | 24 | int adv_state = false; |
| sgetz7908 | 23:7ca590427f0e | 25 | char dev_name[15] = ""; |
| sgetz7908 | 23:7ca590427f0e | 26 | |
| sgetz7908 | 23:7ca590427f0e | 27 | volatile int batt_voltage = 0; // actual battery voltage * 100 |
| sgetz7908 | 23:7ca590427f0e | 28 | |
| sgetz7908 | 23:7ca590427f0e | 29 | bool m_isConnected = false; |
| sgetz7908 | 23:7ca590427f0e | 30 | |
| sgetz7908 | 23:7ca590427f0e | 31 | void process_cmd(char * cmd); |
| sgetz7908 | 23:7ca590427f0e | 32 | |
| sgetz7908 | 32:749e1c060d03 | 33 | void cancel_ble_activity_timeout(void) |
| sgetz7908 | 32:749e1c060d03 | 34 | { |
| sgetz7908 | 32:749e1c060d03 | 35 | if(timeout_id) { eventQueue.cancel(timeout_id); timeout_id = 0; } |
| sgetz7908 | 32:749e1c060d03 | 36 | } |
| sgetz7908 | 23:7ca590427f0e | 37 | |
| sgetz7908 | 23:7ca590427f0e | 38 | void disconnect(void) |
| sgetz7908 | 23:7ca590427f0e | 39 | { |
| sgetz7908 | 32:749e1c060d03 | 40 | cancel_ble_activity_timeout(); |
| sgetz7908 | 23:7ca590427f0e | 41 | #if UART_DEBUGGING==0 |
| sgetz7908 | 23:7ca590427f0e | 42 | BLE &ble = BLE::Instance(); |
| sgetz7908 | 32:749e1c060d03 | 43 | ble.gap().disconnect(Gap::CONNECTION_TIMEOUT ); // force disconnect |
| sgetz7908 | 23:7ca590427f0e | 44 | #endif |
| sgetz7908 | 23:7ca590427f0e | 45 | } |
| sgetz7908 | 23:7ca590427f0e | 46 | |
| sgetz7908 | 32:749e1c060d03 | 47 | void dis(void) |
| sgetz7908 | 23:7ca590427f0e | 48 | { |
| sgetz7908 | 32:749e1c060d03 | 49 | disconnect(); |
| sgetz7908 | 23:7ca590427f0e | 50 | } |
| sgetz7908 | 23:7ca590427f0e | 51 | |
| sgetz7908 | 32:749e1c060d03 | 52 | void ble_activity_timeout(void) |
| sgetz7908 | 32:749e1c060d03 | 53 | { |
| sgetz7908 | 32:749e1c060d03 | 54 | eventQueue.call(dis); |
| sgetz7908 | 32:749e1c060d03 | 55 | } |
| sgetz7908 | 32:749e1c060d03 | 56 | |
| sgetz7908 | 32:749e1c060d03 | 57 | void reset_ble_activity_timer(void) |
| sgetz7908 | 32:749e1c060d03 | 58 | { |
| sgetz7908 | 32:749e1c060d03 | 59 | cancel_ble_activity_timeout(); |
| sgetz7908 | 32:749e1c060d03 | 60 | timeout_id = eventQueue.call_in(BLE_INACTIVITY_TIMEOUT*1000UL, ble_activity_timeout); |
| sgetz7908 | 32:749e1c060d03 | 61 | } |
| sgetz7908 | 32:749e1c060d03 | 62 | |
| sgetz7908 | 32:749e1c060d03 | 63 | void disconnect_happened() |
| sgetz7908 | 23:7ca590427f0e | 64 | { |
| sgetz7908 | 23:7ca590427f0e | 65 | m_isConnected = false; |
| sgetz7908 | 32:749e1c060d03 | 66 | cancel_ble_activity_timeout(); |
| sgetz7908 | 23:7ca590427f0e | 67 | BLE &ble = BLE::Instance(); |
| sgetz7908 | 23:7ca590427f0e | 68 | ble.gap().setAdvertisingInterval(ADV_INTERVAL); |
| sgetz7908 | 23:7ca590427f0e | 69 | if(adv_state) ble.gap().startAdvertising(); |
| sgetz7908 | 23:7ca590427f0e | 70 | } |
| sgetz7908 | 32:749e1c060d03 | 71 | |
| sgetz7908 | 32:749e1c060d03 | 72 | /// Called when the Bluetooth connection is disconnected. |
| sgetz7908 | 32:749e1c060d03 | 73 | void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) |
| sgetz7908 | 32:749e1c060d03 | 74 | { |
| sgetz7908 | 32:749e1c060d03 | 75 | eventQueue.call(disconnect_happened); |
| sgetz7908 | 32:749e1c060d03 | 76 | } |
| sgetz7908 | 23:7ca590427f0e | 77 | |
| sgetz7908 | 32:749e1c060d03 | 78 | void connect_happened(void) |
| sgetz7908 | 23:7ca590427f0e | 79 | { |
| sgetz7908 | 23:7ca590427f0e | 80 | m_isConnected = true; |
| sgetz7908 | 23:7ca590427f0e | 81 | BLE &ble = BLE::Instance(); |
| sgetz7908 | 23:7ca590427f0e | 82 | ble.gap().setAdvertisingInterval(CONN_INTERVAL); |
| sgetz7908 | 32:749e1c060d03 | 83 | reset_ble_activity_timer(); |
| sgetz7908 | 32:749e1c060d03 | 84 | } |
| sgetz7908 | 32:749e1c060d03 | 85 | |
| sgetz7908 | 32:749e1c060d03 | 86 | void connectionCallback(const Gap::ConnectionCallbackParams_t *params) |
| sgetz7908 | 32:749e1c060d03 | 87 | { |
| sgetz7908 | 32:749e1c060d03 | 88 | eventQueue.call(connect_happened); |
| sgetz7908 | 23:7ca590427f0e | 89 | } |
| sgetz7908 | 23:7ca590427f0e | 90 | |
| sgetz7908 | 23:7ca590427f0e | 91 | bool isConnected(void) |
| sgetz7908 | 23:7ca590427f0e | 92 | { |
| sgetz7908 | 23:7ca590427f0e | 93 | return m_isConnected; |
| sgetz7908 | 23:7ca590427f0e | 94 | } |
| sgetz7908 | 23:7ca590427f0e | 95 | |
| sgetz7908 | 23:7ca590427f0e | 96 | /// Called to write a string to the Bluetooth UART. |
| sgetz7908 | 23:7ca590427f0e | 97 | // used for writting to the BLE UART |
| sgetz7908 | 23:7ca590427f0e | 98 | void BLE_UART_xmit(const char * str) |
| sgetz7908 | 23:7ca590427f0e | 99 | { |
| sgetz7908 | 23:7ca590427f0e | 100 | if (uartServicePtr != NULL) { |
| sgetz7908 | 23:7ca590427f0e | 101 | uartServicePtr->writeString(str); |
| sgetz7908 | 23:7ca590427f0e | 102 | } |
| sgetz7908 | 23:7ca590427f0e | 103 | } |
| sgetz7908 | 23:7ca590427f0e | 104 | |
| sgetz7908 | 23:7ca590427f0e | 105 | void BLE_UART_xmit(int i) |
| sgetz7908 | 23:7ca590427f0e | 106 | { |
| sgetz7908 | 23:7ca590427f0e | 107 | BLE_UART_xmit(uli2a(i)); |
| sgetz7908 | 23:7ca590427f0e | 108 | } |
| sgetz7908 | 23:7ca590427f0e | 109 | |
| sgetz7908 | 23:7ca590427f0e | 110 | char cmd_str[21]; |
| sgetz7908 | 23:7ca590427f0e | 111 | |
| sgetz7908 | 23:7ca590427f0e | 112 | /// Called when data is rcv'd from connected BLE device |
| sgetz7908 | 23:7ca590427f0e | 113 | void onDataWritten(const GattWriteCallbackParams *params) |
| sgetz7908 | 23:7ca590427f0e | 114 | { |
| sgetz7908 | 23:7ca590427f0e | 115 | if ((uartServicePtr != NULL) && (params->handle == uartServicePtr->getTXCharacteristicHandle())) { |
| sgetz7908 | 23:7ca590427f0e | 116 | uint16_t bytesRead = params->len; |
| sgetz7908 | 23:7ca590427f0e | 117 | strncpy(cmd_str, (const char *)params->data, bytesRead); |
| sgetz7908 | 24:761c30334cf4 | 118 | cmd_str[bytesRead] = 0; // add end of string char |
| sgetz7908 | 23:7ca590427f0e | 119 | |
| sgetz7908 | 23:7ca590427f0e | 120 | // Start process: process_cmd(cmd_str) as regular non-irq routine |
| sgetz7908 | 23:7ca590427f0e | 121 | eventQueue.call(process_cmd, cmd_str); |
| sgetz7908 | 23:7ca590427f0e | 122 | // the dispatch method executes events |
| sgetz7908 | 23:7ca590427f0e | 123 | //queue.dispatch(); |
| sgetz7908 | 23:7ca590427f0e | 124 | |
| sgetz7908 | 23:7ca590427f0e | 125 | //ble.updateCharacteristicValue(uartServicePtr->getRXCharacteristicHandle(), params->data, bytesRead); // send back whatever is sent to us |
| sgetz7908 | 32:749e1c060d03 | 126 | eventQueue.call(reset_ble_activity_timer); |
| sgetz7908 | 32:749e1c060d03 | 127 | //reset_ble_activity_timer(); |
| sgetz7908 | 23:7ca590427f0e | 128 | } |
| sgetz7908 | 23:7ca590427f0e | 129 | } |
| sgetz7908 | 23:7ca590427f0e | 130 | |
| sgetz7908 | 27:bb7247a1704e | 131 | |
| sgetz7908 | 27:bb7247a1704e | 132 | /// Called when data is rcv'd from connected BLE device |
| sgetz7908 | 27:bb7247a1704e | 133 | void dataReadCallback(const GattReadCallbackParams *params) |
| sgetz7908 | 27:bb7247a1704e | 134 | { |
| sgetz7908 | 27:bb7247a1704e | 135 | //if ((uartServicePtr != NULL) && (params->handle == uartServicePtr->getRXCharacteristicHandle())) { |
| sgetz7908 | 27:bb7247a1704e | 136 | // Start process: process as regular non-irq routine |
| sgetz7908 | 27:bb7247a1704e | 137 | eventQueue.call(dataWasRead); |
| sgetz7908 | 27:bb7247a1704e | 138 | //} |
| sgetz7908 | 27:bb7247a1704e | 139 | } |
| sgetz7908 | 27:bb7247a1704e | 140 | |
| sgetz7908 | 27:bb7247a1704e | 141 | |
| sgetz7908 | 23:7ca590427f0e | 142 | /// Turn on or off advertising |
| sgetz7908 | 23:7ca590427f0e | 143 | // When turned on, it will only advertise for a limited time, or until turned off. |
| sgetz7908 | 23:7ca590427f0e | 144 | void set_radio(int state) |
| sgetz7908 | 23:7ca590427f0e | 145 | { |
| sgetz7908 | 23:7ca590427f0e | 146 | BLE &ble = BLE::Instance(); |
| sgetz7908 | 23:7ca590427f0e | 147 | adv_state = state; |
| sgetz7908 | 23:7ca590427f0e | 148 | |
| sgetz7908 | 23:7ca590427f0e | 149 | if(state) { |
| sgetz7908 | 24:761c30334cf4 | 150 | //adv_state = true; |
| sgetz7908 | 23:7ca590427f0e | 151 | ble.gap().setAdvertisingInterval(ADV_INTERVAL); |
| sgetz7908 | 23:7ca590427f0e | 152 | ble.gap().startAdvertising(); |
| sgetz7908 | 23:7ca590427f0e | 153 | } else { |
| sgetz7908 | 24:761c30334cf4 | 154 | //adv_state = false; |
| sgetz7908 | 23:7ca590427f0e | 155 | ble.gap().stopAdvertising(); |
| sgetz7908 | 23:7ca590427f0e | 156 | disconnect(); |
| sgetz7908 | 23:7ca590427f0e | 157 | } |
| sgetz7908 | 23:7ca590427f0e | 158 | } |
| sgetz7908 | 23:7ca590427f0e | 159 | |
| sgetz7908 | 23:7ca590427f0e | 160 | #if 0 |
| sgetz7908 | 23:7ca590427f0e | 161 | void stop_radio_and_wait(void) |
| sgetz7908 | 23:7ca590427f0e | 162 | { |
| sgetz7908 | 23:7ca590427f0e | 163 | set_radio(false); |
| sgetz7908 | 23:7ca590427f0e | 164 | |
| sgetz7908 | 23:7ca590427f0e | 165 | // If radio is active, wait for it to become inactive. |
| sgetz7908 | 23:7ca590427f0e | 166 | //wait(0.5); |
| sgetz7908 | 23:7ca590427f0e | 167 | while (isConnected())// || isRadioActive()) |
| sgetz7908 | 23:7ca590427f0e | 168 | { |
| sgetz7908 | 23:7ca590427f0e | 169 | // Do nothing (just wait for radio to become inactive). |
| sgetz7908 | 23:7ca590427f0e | 170 | sd_app_evt_wait(); |
| sgetz7908 | 23:7ca590427f0e | 171 | } |
| sgetz7908 | 23:7ca590427f0e | 172 | } |
| sgetz7908 | 23:7ca590427f0e | 173 | #endif |
| sgetz7908 | 23:7ca590427f0e | 174 | |
| sgetz7908 | 27:bb7247a1704e | 175 | void authorize_client_write(GattWriteAuthCallbackParams *e) |
| sgetz7908 | 27:bb7247a1704e | 176 | { |
| sgetz7908 | 27:bb7247a1704e | 177 | e->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; |
| sgetz7908 | 27:bb7247a1704e | 178 | } |
| sgetz7908 | 27:bb7247a1704e | 179 | |
| sgetz7908 | 23:7ca590427f0e | 180 | ble_init_status_t ble_init_status = BLE_INIT_IN_PROGRESS; |
| sgetz7908 | 23:7ca590427f0e | 181 | |
| sgetz7908 | 23:7ca590427f0e | 182 | /// Called to setup the Bluetooth link. |
| sgetz7908 | 23:7ca590427f0e | 183 | void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) |
| sgetz7908 | 23:7ca590427f0e | 184 | { |
| sgetz7908 | 23:7ca590427f0e | 185 | BLE& ble = params->ble; |
| sgetz7908 | 23:7ca590427f0e | 186 | ble_error_t error = params->error; |
| sgetz7908 | 23:7ca590427f0e | 187 | |
| sgetz7908 | 23:7ca590427f0e | 188 | if (error != BLE_ERROR_NONE) { |
| sgetz7908 | 23:7ca590427f0e | 189 | ble_init_status = BLE_INIT_ERROR; |
| sgetz7908 | 23:7ca590427f0e | 190 | return; |
| sgetz7908 | 23:7ca590427f0e | 191 | } |
| sgetz7908 | 23:7ca590427f0e | 192 | |
| sgetz7908 | 23:7ca590427f0e | 193 | /* Ensure that it is the default instance of BLE */ |
| sgetz7908 | 23:7ca590427f0e | 194 | if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) { |
| sgetz7908 | 23:7ca590427f0e | 195 | return; |
| sgetz7908 | 23:7ca590427f0e | 196 | } |
| sgetz7908 | 23:7ca590427f0e | 197 | |
| sgetz7908 | 23:7ca590427f0e | 198 | ble.gap().onDisconnection(disconnectionCallback); |
| sgetz7908 | 23:7ca590427f0e | 199 | ble.gap().onConnection(connectionCallback); |
| sgetz7908 | 23:7ca590427f0e | 200 | ble.gattServer().onDataWritten(onDataWritten); |
| sgetz7908 | 23:7ca590427f0e | 201 | |
| sgetz7908 | 23:7ca590427f0e | 202 | //ble.gattServer().onDataRead( dataReadCallback ); //const DataReadCallback_t & callback) |
| sgetz7908 | 23:7ca590427f0e | 203 | //ble.onDataRead( dataReadCallback); |
| sgetz7908 | 23:7ca590427f0e | 204 | |
| sgetz7908 | 23:7ca590427f0e | 205 | strcpy(dev_name, DEV_NAME); |
| sgetz7908 | 30:76b51e525c40 | 206 | //strcat(dev_name, char2hex(NRF_FICR->DEVICEADDR[0] & 0xfff, 3)); |
| sgetz7908 | 30:76b51e525c40 | 207 | strcat(dev_name, char2hex(NRF_FICR->DEVICEADDR[1] | 0xc000, 4)); |
| sgetz7908 | 30:76b51e525c40 | 208 | strcat(dev_name, char2hex(NRF_FICR->DEVICEADDR[0], 8)); |
| sgetz7908 | 23:7ca590427f0e | 209 | int len = strlen(dev_name); |
| sgetz7908 | 23:7ca590427f0e | 210 | |
| sgetz7908 | 23:7ca590427f0e | 211 | ble.gap().setDeviceName((std::uint8_t *)dev_name); |
| sgetz7908 | 23:7ca590427f0e | 212 | |
| sgetz7908 | 23:7ca590427f0e | 213 | /* Setup primary service */ |
| sgetz7908 | 23:7ca590427f0e | 214 | uartServicePtr = new UARTService(ble); |
| sgetz7908 | 23:7ca590427f0e | 215 | |
| sgetz7908 | 23:7ca590427f0e | 216 | /* setup advertising */ |
| sgetz7908 | 23:7ca590427f0e | 217 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED); |
| sgetz7908 | 23:7ca590427f0e | 218 | ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); |
| sgetz7908 | 23:7ca590427f0e | 219 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME, |
| sgetz7908 | 23:7ca590427f0e | 220 | (const uint8_t *)dev_name, len); |
| sgetz7908 | 30:76b51e525c40 | 221 | //ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, |
| sgetz7908 | 30:76b51e525c40 | 222 | // (const uint8_t *)UARTServiceUUID_reversed, sizeof(UARTServiceUUID_reversed)); |
| sgetz7908 | 23:7ca590427f0e | 223 | |
| sgetz7908 | 23:7ca590427f0e | 224 | |
| sgetz7908 | 23:7ca590427f0e | 225 | ble.gap().setAdvertisingInterval(ADV_INTERVAL); |
| sgetz7908 | 23:7ca590427f0e | 226 | ble.gap().setAdvertisingTimeout(BLE_ADVERTISING_DURATION); |
| sgetz7908 | 23:7ca590427f0e | 227 | ble.gap().setTxPower(-20); // -30, -20, -16, -12, -8, -4, 0, and 4 dBm |
| sgetz7908 | 23:7ca590427f0e | 228 | |
| sgetz7908 | 23:7ca590427f0e | 229 | ble_init_status = BLE_INIT_OK; |
| sgetz7908 | 23:7ca590427f0e | 230 | |
| sgetz7908 | 23:7ca590427f0e | 231 | set_radio(true); |
| sgetz7908 | 23:7ca590427f0e | 232 | } |
| sgetz7908 | 23:7ca590427f0e | 233 | |
| sgetz7908 | 23:7ca590427f0e | 234 | /// Process BLE Events |
| sgetz7908 | 23:7ca590427f0e | 235 | void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) { |
| sgetz7908 | 23:7ca590427f0e | 236 | BLE &ble = BLE::Instance(); |
| sgetz7908 | 23:7ca590427f0e | 237 | eventQueue.call(Callback<void()>(&ble, &BLE::processEvents)); |
| sgetz7908 | 23:7ca590427f0e | 238 | } |
| sgetz7908 | 23:7ca590427f0e | 239 | |
| sgetz7908 | 23:7ca590427f0e | 240 | /// Initialize the BLE Stack |
| sgetz7908 | 23:7ca590427f0e | 241 | // Returns 0 if OK |
| sgetz7908 | 23:7ca590427f0e | 242 | ble_init_status_t Init_BLE_Stuff(void) |
| sgetz7908 | 23:7ca590427f0e | 243 | { |
| sgetz7908 | 23:7ca590427f0e | 244 | ble_init_status = BLE_INIT_IN_PROGRESS; |
| sgetz7908 | 23:7ca590427f0e | 245 | BLE &ble = BLE::Instance(); |
| sgetz7908 | 23:7ca590427f0e | 246 | ble.onEventsToProcess(scheduleBleEventsProcessing); |
| sgetz7908 | 23:7ca590427f0e | 247 | ble.init(bleInitComplete); |
| sgetz7908 | 23:7ca590427f0e | 248 | |
| sgetz7908 | 23:7ca590427f0e | 249 | // Wait for initialization to complete. This is necessary because the |
| sgetz7908 | 23:7ca590427f0e | 250 | // BLE object is used after this. |
| sgetz7908 | 23:7ca590427f0e | 251 | while(ble_init_status == BLE_INIT_IN_PROGRESS) { wait(0.05);} |
| sgetz7908 | 23:7ca590427f0e | 252 | // set up BLE Information services in infoService.cpp |
| sgetz7908 | 23:7ca590427f0e | 253 | //ble.gattServer().addService(infoServicePtr); |
| sgetz7908 | 23:7ca590427f0e | 254 | return ble_init_status; |
| sgetz7908 | 23:7ca590427f0e | 255 | } |