prototype.
Dependencies: BLE_API_Native_IRC TMP102 beep mbed
Fork of BLE_Health_Thermometer_IRC by
main.cpp@5:01a978ee3cc1, 2014-08-25 (annotated)
- Committer:
- MasanoriSakai
- Date:
- Mon Aug 25 13:42:31 2014 +0000
- Revision:
- 5:01a978ee3cc1
- Parent:
- 2:f11df1469db2
First revision from sample program.;
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
donalm | 0:701da26ee5cb | 1 | /* mbed Microcontroller Library |
donalm | 0:701da26ee5cb | 2 | * Copyright (c) 2006-2014 ARM Limited |
donalm | 0:701da26ee5cb | 3 | * |
donalm | 0:701da26ee5cb | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
donalm | 0:701da26ee5cb | 5 | * you may not use this file except in compliance with the License. |
donalm | 0:701da26ee5cb | 6 | * You may obtain a copy of the License at |
donalm | 0:701da26ee5cb | 7 | * |
donalm | 0:701da26ee5cb | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
donalm | 0:701da26ee5cb | 9 | * |
donalm | 0:701da26ee5cb | 10 | * Unless required by applicable law or agreed to in writing, software |
donalm | 0:701da26ee5cb | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
donalm | 0:701da26ee5cb | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
donalm | 0:701da26ee5cb | 13 | * See the License for the specific language governing permissions and |
donalm | 0:701da26ee5cb | 14 | * limitations under the License. |
donalm | 0:701da26ee5cb | 15 | */ |
donalm | 0:701da26ee5cb | 16 | |
donalm | 0:701da26ee5cb | 17 | #include "mbed.h" |
donalm | 0:701da26ee5cb | 18 | #include "TMP102.h" |
donalm | 0:701da26ee5cb | 19 | #include "nRF51822n.h" |
MasanoriSakai | 5:01a978ee3cc1 | 20 | #include "beep.h" |
donalm | 0:701da26ee5cb | 21 | |
donalm | 0:701da26ee5cb | 22 | nRF51822n nrf; /* BLE radio driver */ |
donalm | 0:701da26ee5cb | 23 | TMP102 healthThemometer(p22, p20, 0x90); /* The TMP102 connected to our board */ |
MasanoriSakai | 5:01a978ee3cc1 | 24 | I2C i2c(p22,p20); |
MasanoriSakai | 5:01a978ee3cc1 | 25 | Beep buzzer(p15); |
donalm | 0:701da26ee5cb | 26 | |
donalm | 2:f11df1469db2 | 27 | /* LEDs for indication: */ |
donalm | 2:f11df1469db2 | 28 | DigitalOut oneSecondLed(LED1); /* LED1 is toggled every second. */ |
donalm | 2:f11df1469db2 | 29 | DigitalOut advertisingStateLed(LED2); /* LED2 is on when we are advertising, otherwise off. */ |
donalm | 0:701da26ee5cb | 30 | |
donalm | 0:701da26ee5cb | 31 | |
MasanoriSakai | 5:01a978ee3cc1 | 32 | |
donalm | 0:701da26ee5cb | 33 | /* Health Thermometer Service */ |
donalm | 0:701da26ee5cb | 34 | uint8_t thermTempPayload[5] = { 0, 0, 0, 0, 0 }; |
donalm | 0:701da26ee5cb | 35 | GattService thermService (GattService::UUID_HEALTH_THERMOMETER_SERVICE); |
donalm | 0:701da26ee5cb | 36 | GattCharacteristic thermTemp (GattCharacteristic::UUID_TEMPERATURE_MEASUREMENT_CHAR, |
donalm | 0:701da26ee5cb | 37 | 5, 5, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE); |
donalm | 0:701da26ee5cb | 38 | |
donalm | 0:701da26ee5cb | 39 | /* Battery Level Service */ |
donalm | 0:701da26ee5cb | 40 | uint8_t batt = 100; /* Battery level */ |
donalm | 0:701da26ee5cb | 41 | uint8_t read_batt = 0; /* Variable to hold battery level reads */ |
donalm | 0:701da26ee5cb | 42 | GattService battService ( GattService::UUID_BATTERY_SERVICE ); |
donalm | 0:701da26ee5cb | 43 | GattCharacteristic battLevel ( GattCharacteristic::UUID_BATTERY_LEVEL_CHAR, 1, 1, |
donalm | 0:701da26ee5cb | 44 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY | |
donalm | 0:701da26ee5cb | 45 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ); |
donalm | 0:701da26ee5cb | 46 | |
donalm | 0:701da26ee5cb | 47 | |
donalm | 0:701da26ee5cb | 48 | /* Advertising data and parameters */ |
donalm | 0:701da26ee5cb | 49 | GapAdvertisingData advData; |
donalm | 0:701da26ee5cb | 50 | GapAdvertisingData scanResponse; |
donalm | 0:701da26ee5cb | 51 | GapAdvertisingParams advParams ( GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED ); |
donalm | 2:f11df1469db2 | 52 | |
donalm | 0:701da26ee5cb | 53 | uint16_t uuid16_list[] = {GattService::UUID_HEALTH_THERMOMETER_SERVICE, |
donalm | 0:701da26ee5cb | 54 | GattService::UUID_BATTERY_SERVICE}; |
donalm | 0:701da26ee5cb | 55 | |
donalm | 0:701da26ee5cb | 56 | uint32_t quick_ieee11073_from_float(float temperature); |
donalm | 0:701da26ee5cb | 57 | void updateServiceValues(void); |
donalm | 0:701da26ee5cb | 58 | |
MasanoriSakai | 5:01a978ee3cc1 | 59 | /* |
MasanoriSakai | 5:01a978ee3cc1 | 60 | I2C LCD |
MasanoriSakai | 5:01a978ee3cc1 | 61 | */ |
MasanoriSakai | 5:01a978ee3cc1 | 62 | const int AQCM0802_addr = 0x7C; |
MasanoriSakai | 5:01a978ee3cc1 | 63 | |
MasanoriSakai | 5:01a978ee3cc1 | 64 | unsigned char mode; |
MasanoriSakai | 5:01a978ee3cc1 | 65 | unsigned char contrast = 35; // 0-63 |
MasanoriSakai | 5:01a978ee3cc1 | 66 | unsigned char contrastFlag = false; |
MasanoriSakai | 5:01a978ee3cc1 | 67 | int CGcounter; |
MasanoriSakai | 5:01a978ee3cc1 | 68 | int FADEcounter; |
MasanoriSakai | 5:01a978ee3cc1 | 69 | |
MasanoriSakai | 5:01a978ee3cc1 | 70 | void lcd_cmd(char x) { |
MasanoriSakai | 5:01a978ee3cc1 | 71 | char data[2]; |
MasanoriSakai | 5:01a978ee3cc1 | 72 | data[0] = 0x00; // CO = 0,RS = 0 |
MasanoriSakai | 5:01a978ee3cc1 | 73 | data[1] = x; |
MasanoriSakai | 5:01a978ee3cc1 | 74 | i2c.write(AQCM0802_addr, data, 2); |
MasanoriSakai | 5:01a978ee3cc1 | 75 | } |
MasanoriSakai | 5:01a978ee3cc1 | 76 | |
MasanoriSakai | 5:01a978ee3cc1 | 77 | void lcd_contdata(char x) { |
MasanoriSakai | 5:01a978ee3cc1 | 78 | char data[2]; |
MasanoriSakai | 5:01a978ee3cc1 | 79 | data[0] = 0xC0; //0b11000000 CO = 1, RS = 1 |
MasanoriSakai | 5:01a978ee3cc1 | 80 | data[1] = x; |
MasanoriSakai | 5:01a978ee3cc1 | 81 | i2c.write(AQCM0802_addr, data, 2); |
MasanoriSakai | 5:01a978ee3cc1 | 82 | } |
MasanoriSakai | 5:01a978ee3cc1 | 83 | |
MasanoriSakai | 5:01a978ee3cc1 | 84 | void lcd_lastdata(char x) { |
MasanoriSakai | 5:01a978ee3cc1 | 85 | char data[2]; |
MasanoriSakai | 5:01a978ee3cc1 | 86 | data[0] = 0x40; //0b11000000 CO = 0, RS = 1 |
MasanoriSakai | 5:01a978ee3cc1 | 87 | data[1] = x; |
MasanoriSakai | 5:01a978ee3cc1 | 88 | i2c.write(AQCM0802_addr, data, 2); |
MasanoriSakai | 5:01a978ee3cc1 | 89 | } |
MasanoriSakai | 5:01a978ee3cc1 | 90 | |
MasanoriSakai | 5:01a978ee3cc1 | 91 | void lcd_printStr(const char *s) { |
MasanoriSakai | 5:01a978ee3cc1 | 92 | while(*s) { |
MasanoriSakai | 5:01a978ee3cc1 | 93 | if(*(s + 1)) { |
MasanoriSakai | 5:01a978ee3cc1 | 94 | lcd_contdata(*s); |
MasanoriSakai | 5:01a978ee3cc1 | 95 | } else { |
MasanoriSakai | 5:01a978ee3cc1 | 96 | lcd_lastdata(*s); |
MasanoriSakai | 5:01a978ee3cc1 | 97 | } |
MasanoriSakai | 5:01a978ee3cc1 | 98 | s++; |
MasanoriSakai | 5:01a978ee3cc1 | 99 | } |
MasanoriSakai | 5:01a978ee3cc1 | 100 | } |
MasanoriSakai | 5:01a978ee3cc1 | 101 | |
MasanoriSakai | 5:01a978ee3cc1 | 102 | void lcd_printHex(unsigned char num) { |
MasanoriSakai | 5:01a978ee3cc1 | 103 | lcd_contdata(num); |
MasanoriSakai | 5:01a978ee3cc1 | 104 | } |
MasanoriSakai | 5:01a978ee3cc1 | 105 | |
MasanoriSakai | 5:01a978ee3cc1 | 106 | void lcd_init() { |
MasanoriSakai | 5:01a978ee3cc1 | 107 | wait(0.04); |
MasanoriSakai | 5:01a978ee3cc1 | 108 | // LCD initialize |
MasanoriSakai | 5:01a978ee3cc1 | 109 | lcd_cmd(0x38); // function set |
MasanoriSakai | 5:01a978ee3cc1 | 110 | lcd_cmd(0x39); // function set |
MasanoriSakai | 5:01a978ee3cc1 | 111 | lcd_cmd(0x04); // EntryModeSet |
MasanoriSakai | 5:01a978ee3cc1 | 112 | lcd_cmd(0x14); // interval osc |
MasanoriSakai | 5:01a978ee3cc1 | 113 | lcd_cmd(0x70 | (contrast & 0xF)); // contrast Low |
MasanoriSakai | 5:01a978ee3cc1 | 114 | lcd_cmd(0x5C | ((contrast >> 4) & 0x3)); // contast High/icon/power |
MasanoriSakai | 5:01a978ee3cc1 | 115 | lcd_cmd(0x6C); // follower control |
MasanoriSakai | 5:01a978ee3cc1 | 116 | wait(0.2); |
MasanoriSakai | 5:01a978ee3cc1 | 117 | lcd_cmd(0x38); // function set |
MasanoriSakai | 5:01a978ee3cc1 | 118 | lcd_cmd(0x0C); // Display On |
MasanoriSakai | 5:01a978ee3cc1 | 119 | lcd_cmd(0x01); // Clear Display |
MasanoriSakai | 5:01a978ee3cc1 | 120 | wait(0.2); // need additional wait to Clear Display |
MasanoriSakai | 5:01a978ee3cc1 | 121 | } |
MasanoriSakai | 5:01a978ee3cc1 | 122 | |
MasanoriSakai | 5:01a978ee3cc1 | 123 | void lcd_setCursor(unsigned char x,unsigned char y) { |
MasanoriSakai | 5:01a978ee3cc1 | 124 | lcd_cmd(0x80 | (y * 0x40 + x)); |
MasanoriSakai | 5:01a978ee3cc1 | 125 | } |
MasanoriSakai | 5:01a978ee3cc1 | 126 | |
MasanoriSakai | 5:01a978ee3cc1 | 127 | void setContrast(unsigned char c) { |
MasanoriSakai | 5:01a978ee3cc1 | 128 | lcd_cmd(0x39); |
MasanoriSakai | 5:01a978ee3cc1 | 129 | lcd_cmd(0x70 | (c & 0x0f)); // contrast Low |
MasanoriSakai | 5:01a978ee3cc1 | 130 | lcd_cmd(0x5C | ((c >> 4) & 0x03)); // contast High/icon/power |
MasanoriSakai | 5:01a978ee3cc1 | 131 | lcd_cmd(0x38); |
MasanoriSakai | 5:01a978ee3cc1 | 132 | } |
MasanoriSakai | 5:01a978ee3cc1 | 133 | |
MasanoriSakai | 5:01a978ee3cc1 | 134 | |
donalm | 0:701da26ee5cb | 135 | /**************************************************************************/ |
donalm | 0:701da26ee5cb | 136 | /*! |
donalm | 0:701da26ee5cb | 137 | @brief This custom class can be used to override any GapEvents |
donalm | 0:701da26ee5cb | 138 | that you are interested in handling on an application level. |
donalm | 0:701da26ee5cb | 139 | */ |
donalm | 0:701da26ee5cb | 140 | /**************************************************************************/ |
donalm | 0:701da26ee5cb | 141 | class GapEventHandler : public GapEvents |
donalm | 0:701da26ee5cb | 142 | { |
donalm | 2:f11df1469db2 | 143 | //virtual void onTimeout(void) {} |
donalm | 2:f11df1469db2 | 144 | |
donalm | 0:701da26ee5cb | 145 | virtual void onConnected(void) |
donalm | 0:701da26ee5cb | 146 | { |
MasanoriSakai | 5:01a978ee3cc1 | 147 | advertisingStateLed = 1; |
MasanoriSakai | 5:01a978ee3cc1 | 148 | buzzer.beep(440000.0,1); |
MasanoriSakai | 5:01a978ee3cc1 | 149 | |
donalm | 0:701da26ee5cb | 150 | } |
donalm | 0:701da26ee5cb | 151 | |
donalm | 2:f11df1469db2 | 152 | /* When a client device disconnects we need to start advertising again. */ |
donalm | 0:701da26ee5cb | 153 | virtual void onDisconnected(void) |
donalm | 0:701da26ee5cb | 154 | { |
donalm | 0:701da26ee5cb | 155 | nrf.getGap().startAdvertising(advParams); |
MasanoriSakai | 5:01a978ee3cc1 | 156 | advertisingStateLed = 0; |
donalm | 0:701da26ee5cb | 157 | } |
donalm | 0:701da26ee5cb | 158 | }; |
donalm | 0:701da26ee5cb | 159 | |
donalm | 0:701da26ee5cb | 160 | /**************************************************************************/ |
donalm | 0:701da26ee5cb | 161 | /*! |
donalm | 0:701da26ee5cb | 162 | @brief Program entry point |
donalm | 0:701da26ee5cb | 163 | */ |
donalm | 0:701da26ee5cb | 164 | /**************************************************************************/ |
donalm | 0:701da26ee5cb | 165 | int main(void) |
MasanoriSakai | 5:01a978ee3cc1 | 166 | { |
donalm | 2:f11df1469db2 | 167 | /* Setup blinky led */ |
donalm | 2:f11df1469db2 | 168 | oneSecondLed=1; |
donalm | 0:701da26ee5cb | 169 | |
donalm | 2:f11df1469db2 | 170 | /* Setup an event handler for GAP events i.e. Client/Server connection events. */ |
donalm | 0:701da26ee5cb | 171 | nrf.getGap().setEventHandler(new GapEventHandler()); |
donalm | 2:f11df1469db2 | 172 | |
donalm | 0:701da26ee5cb | 173 | /* Initialise the nRF51822 */ |
donalm | 0:701da26ee5cb | 174 | nrf.init(); |
donalm | 0:701da26ee5cb | 175 | |
donalm | 0:701da26ee5cb | 176 | /* Make sure we get a clean start */ |
donalm | 0:701da26ee5cb | 177 | nrf.reset(); |
donalm | 0:701da26ee5cb | 178 | |
donalm | 0:701da26ee5cb | 179 | /* Add BLE-Only flag and complete service list to the advertising data */ |
donalm | 0:701da26ee5cb | 180 | advData.addFlags(GapAdvertisingData::BREDR_NOT_SUPPORTED); |
donalm | 0:701da26ee5cb | 181 | advData.addData(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, |
donalm | 0:701da26ee5cb | 182 | (uint8_t*)uuid16_list, sizeof(uuid16_list)); |
donalm | 0:701da26ee5cb | 183 | advData.addAppearance(GapAdvertisingData::GENERIC_THERMOMETER); |
donalm | 0:701da26ee5cb | 184 | nrf.getGap().setAdvertisingData(advData, scanResponse); |
donalm | 0:701da26ee5cb | 185 | |
donalm | 0:701da26ee5cb | 186 | /* Health Thermometer Service */ |
donalm | 0:701da26ee5cb | 187 | thermService.addCharacteristic(thermTemp); |
donalm | 0:701da26ee5cb | 188 | nrf.getGattServer().addService(thermService); |
donalm | 0:701da26ee5cb | 189 | |
donalm | 0:701da26ee5cb | 190 | /* Add the Battery Level service */ |
donalm | 0:701da26ee5cb | 191 | battService.addCharacteristic(battLevel); |
donalm | 0:701da26ee5cb | 192 | nrf.getGattServer().addService(battService); |
donalm | 0:701da26ee5cb | 193 | |
donalm | 0:701da26ee5cb | 194 | /* Start advertising (make sure you've added all your data first) */ |
donalm | 0:701da26ee5cb | 195 | nrf.getGap().startAdvertising(advParams); |
MasanoriSakai | 5:01a978ee3cc1 | 196 | advertisingStateLed = 0; |
MasanoriSakai | 5:01a978ee3cc1 | 197 | |
MasanoriSakai | 5:01a978ee3cc1 | 198 | /* i2c lcd initialize */ |
MasanoriSakai | 5:01a978ee3cc1 | 199 | lcd_init(); |
MasanoriSakai | 5:01a978ee3cc1 | 200 | lcd_setCursor(0, 0); |
MasanoriSakai | 5:01a978ee3cc1 | 201 | lcd_printStr("Start "); |
donalm | 0:701da26ee5cb | 202 | |
donalm | 0:701da26ee5cb | 203 | for (;;) |
donalm | 0:701da26ee5cb | 204 | { |
donalm | 2:f11df1469db2 | 205 | /* Now that we're live, update the battery level & temperature characteristics */ |
donalm | 2:f11df1469db2 | 206 | updateServiceValues(); |
donalm | 0:701da26ee5cb | 207 | wait(1); |
donalm | 0:701da26ee5cb | 208 | } |
donalm | 0:701da26ee5cb | 209 | } |
donalm | 0:701da26ee5cb | 210 | |
donalm | 0:701da26ee5cb | 211 | /**************************************************************************/ |
donalm | 0:701da26ee5cb | 212 | /*! |
donalm | 2:f11df1469db2 | 213 | @brief Ticker callback to switch advertisingStateLed state |
donalm | 0:701da26ee5cb | 214 | */ |
donalm | 0:701da26ee5cb | 215 | /**************************************************************************/ |
donalm | 0:701da26ee5cb | 216 | void updateServiceValues(void) |
donalm | 0:701da26ee5cb | 217 | { |
MasanoriSakai | 5:01a978ee3cc1 | 218 | char tempstring[6]; |
MasanoriSakai | 5:01a978ee3cc1 | 219 | AnalogIn thermister(p2); |
MasanoriSakai | 5:01a978ee3cc1 | 220 | float B=4250,T0=298.15,R0=100.0,R1=10,rr1,t; |
MasanoriSakai | 5:01a978ee3cc1 | 221 | int n; |
MasanoriSakai | 5:01a978ee3cc1 | 222 | |
donalm | 2:f11df1469db2 | 223 | /* Toggle the one second LEDs */ |
donalm | 2:f11df1469db2 | 224 | oneSecondLed = !oneSecondLed; |
donalm | 0:701da26ee5cb | 225 | |
donalm | 0:701da26ee5cb | 226 | /* Update battery level */ |
donalm | 0:701da26ee5cb | 227 | nrf.getGattServer().updateValue(battLevel.handle, (uint8_t*)&batt, sizeof(batt)); |
donalm | 2:f11df1469db2 | 228 | /* Decrement the battery level. */ |
donalm | 2:f11df1469db2 | 229 | batt <=50 ? batt=100 : batt--;; |
MasanoriSakai | 5:01a978ee3cc1 | 230 | |
MasanoriSakai | 5:01a978ee3cc1 | 231 | n = thermister.read_u16(); |
MasanoriSakai | 5:01a978ee3cc1 | 232 | rr1=R1*n/(1023.0-n); |
MasanoriSakai | 5:01a978ee3cc1 | 233 | t=1/(log(rr1/R0)/B+(1/T0)); |
MasanoriSakai | 5:01a978ee3cc1 | 234 | |
donalm | 2:f11df1469db2 | 235 | /* Update the temperature. Note that we need to convert to an ieee11073 format float. */ |
MasanoriSakai | 5:01a978ee3cc1 | 236 | float temperature = healthThemometer.read(); |
MasanoriSakai | 5:01a978ee3cc1 | 237 | uint32_t temp_ieee11073 = quick_ieee11073_from_float(t-275.15); |
donalm | 0:701da26ee5cb | 238 | memcpy(thermTempPayload+1, &temp_ieee11073, 4); |
donalm | 0:701da26ee5cb | 239 | nrf.getGattServer().updateValue(thermTemp.handle, thermTempPayload, sizeof(thermTempPayload)); |
MasanoriSakai | 5:01a978ee3cc1 | 240 | |
MasanoriSakai | 5:01a978ee3cc1 | 241 | lcd_setCursor(0,0); |
MasanoriSakai | 5:01a978ee3cc1 | 242 | lcd_printStr(" "); |
MasanoriSakai | 5:01a978ee3cc1 | 243 | sprintf(tempstring,"T=%05.2f", t-275.15); |
MasanoriSakai | 5:01a978ee3cc1 | 244 | lcd_setCursor(0,0); |
MasanoriSakai | 5:01a978ee3cc1 | 245 | lcd_printStr(tempstring); |
MasanoriSakai | 5:01a978ee3cc1 | 246 | |
MasanoriSakai | 5:01a978ee3cc1 | 247 | sprintf(tempstring,"S=%05.2f",temperature); |
MasanoriSakai | 5:01a978ee3cc1 | 248 | lcd_setCursor(0,1); |
MasanoriSakai | 5:01a978ee3cc1 | 249 | lcd_printStr(" "); |
MasanoriSakai | 5:01a978ee3cc1 | 250 | lcd_setCursor(0,1); |
MasanoriSakai | 5:01a978ee3cc1 | 251 | lcd_printStr(tempstring); |
MasanoriSakai | 5:01a978ee3cc1 | 252 | |
donalm | 0:701da26ee5cb | 253 | } |
donalm | 0:701da26ee5cb | 254 | |
donalm | 0:701da26ee5cb | 255 | /** |
donalm | 0:701da26ee5cb | 256 | * @brief A very quick conversion between a float temperature and 11073-20601 FLOAT-Type. |
donalm | 0:701da26ee5cb | 257 | * @param temperature The temperature as a float. |
donalm | 0:701da26ee5cb | 258 | * @return The temperature in 11073-20601 FLOAT-Type format. |
donalm | 0:701da26ee5cb | 259 | */ |
donalm | 0:701da26ee5cb | 260 | uint32_t quick_ieee11073_from_float(float temperature) |
donalm | 0:701da26ee5cb | 261 | { |
donalm | 0:701da26ee5cb | 262 | uint8_t exponent = 0xFF; //exponent is -1 |
donalm | 0:701da26ee5cb | 263 | uint32_t mantissa = (uint32_t)(temperature*10); |
donalm | 0:701da26ee5cb | 264 | |
donalm | 0:701da26ee5cb | 265 | return ( ((uint32_t)exponent) << 24) | mantissa; |
donalm | 2:f11df1469db2 | 266 | } |
donalm | 2:f11df1469db2 | 267 |