MAX30100 pulse rate sensor

Dependencies:   PulseRate

Dependents:   PulseRate

Committer:
kohlerba
Date:
Sun Nov 26 21:59:11 2017 +0000
Revision:
4:008e40a7d035
Parent:
3:fa37b0c705b3
Header file error fix

Who changed what in which revision?

UserRevisionLine numberNew contents of line
kohlerba 0:faf977308bdc 1 #ifndef MAX30100_H
kohlerba 0:faf977308bdc 2 #define MAX30100_H
kohlerba 0:faf977308bdc 3
kohlerba 0:faf977308bdc 4 #include "mbed.h"
kohlerba 0:faf977308bdc 5
kohlerba 0:faf977308bdc 6 //definitions
kohlerba 3:fa37b0c705b3 7 #define MAX30100_ADDRESS 0xAE
kohlerba 0:faf977308bdc 8
kohlerba 0:faf977308bdc 9 // Registers
kohlerba 0:faf977308bdc 10 #define MAX30100_INT_STATUS 0x00 // Which interrupts are tripped
kohlerba 0:faf977308bdc 11 #define MAX30100_INT_ENABLE 0x01 // Which interrupts are active
kohlerba 0:faf977308bdc 12 #define MAX30100_FIFO_WR_PTR 0x02 // Where data is being written
kohlerba 0:faf977308bdc 13 #define MAX30100_OVRFLOW_CTR 0x03 // Number of lost samples
kohlerba 0:faf977308bdc 14 #define MAX30100_FIFO_RD_PTR 0x04 // Where to read from
kohlerba 0:faf977308bdc 15 #define MAX30100_FIFO_DATA 0x05 // Ouput data buffer
kohlerba 0:faf977308bdc 16 #define MAX30100_MODE_CONFIG 0x06 // Control register
kohlerba 0:faf977308bdc 17 #define MAX30100_SPO2_CONFIG 0x07 // Oximetry settings
kohlerba 0:faf977308bdc 18 #define MAX30100_LED_CONFIG 0x09 // Pulse width and power of LEDs
kohlerba 0:faf977308bdc 19 #define MAX30100_TEMP_INTG 0x16 // Temperature value, whole number
kohlerba 0:faf977308bdc 20 #define MAX30100_TEMP_FRAC 0x17 // Temperature value, fraction
kohlerba 0:faf977308bdc 21 #define MAX30100_REV_ID 0xFE // Part revision
kohlerba 0:faf977308bdc 22 #define MAX30100_PART_ID 0xFF // Part ID, normally 0x11
kohlerba 0:faf977308bdc 23
kohlerba 2:d329886938f1 24 #define POR_PART_ID 0x11
kohlerba 2:d329886938f1 25
kohlerba 4:008e40a7d035 26 //Mode Configuration register
kohlerba 4:008e40a7d035 27 #define MAX30100_MC_TEMP_EN (1 << 3)
kohlerba 4:008e40a7d035 28 #define MAX30100_MC_RESET (1 << 6)
kohlerba 4:008e40a7d035 29 #define MAX30100_MC_SHDN (1 << 7)
kohlerba 4:008e40a7d035 30
kohlerba 4:008e40a7d035 31 typedef enum Mode {
kohlerba 4:008e40a7d035 32 MAX30100_MODE_HRONLY = 0x02,
kohlerba 4:008e40a7d035 33 MAX30100_MODE_SPO2_HR = 0x03
kohlerba 4:008e40a7d035 34 } Mode;
kohlerba 0:faf977308bdc 35
kohlerba 4:008e40a7d035 36 // SpO2 Configuration register
kohlerba 4:008e40a7d035 37 // Check tables 8 and 9, p19 of the MAX30100 datasheet to see the permissible
kohlerba 4:008e40a7d035 38 // combinations of sampling rates and pulse widths
kohlerba 4:008e40a7d035 39 #define MAX30100_SPC_SPO2_HI_RES_EN (1 << 6)
kohlerba 4:008e40a7d035 40 typedef enum SamplingRate {
kohlerba 4:008e40a7d035 41 MAX30100_SAMPRATE_50HZ = 0x00,
kohlerba 4:008e40a7d035 42 MAX30100_SAMPRATE_100HZ = 0x01,
kohlerba 4:008e40a7d035 43 MAX30100_SAMPRATE_167HZ = 0x02,
kohlerba 4:008e40a7d035 44 MAX30100_SAMPRATE_200HZ = 0x03,
kohlerba 4:008e40a7d035 45 MAX30100_SAMPRATE_400HZ = 0x04,
kohlerba 4:008e40a7d035 46 MAX30100_SAMPRATE_600HZ = 0x05,
kohlerba 4:008e40a7d035 47 MAX30100_SAMPRATE_800HZ = 0x06,
kohlerba 4:008e40a7d035 48 MAX30100_SAMPRATE_1000HZ = 0x07
kohlerba 4:008e40a7d035 49 } SamplingRate;
kohlerba 0:faf977308bdc 50
kohlerba 4:008e40a7d035 51 typedef enum LEDPulseWidth {
kohlerba 4:008e40a7d035 52 MAX30100_SPC_PW_200US_13BITS = 0x00,
kohlerba 4:008e40a7d035 53 MAX30100_SPC_PW_400US_14BITS = 0x01,
kohlerba 4:008e40a7d035 54 MAX30100_SPC_PW_800US_15BITS = 0x02,
kohlerba 4:008e40a7d035 55 MAX30100_SPC_PW_1600US_16BITS = 0x03
kohlerba 4:008e40a7d035 56 } LEDPulseWidth;
kohlerba 4:008e40a7d035 57
kohlerba 4:008e40a7d035 58 // LED Configuration register
kohlerba 4:008e40a7d035 59 typedef enum LEDCurrent {
kohlerba 4:008e40a7d035 60 MAX30100_LED_CURR_0MA = 0x00,
kohlerba 4:008e40a7d035 61 MAX30100_LED_CURR_4_4MA = 0x01,
kohlerba 4:008e40a7d035 62 MAX30100_LED_CURR_7_6MA = 0x02,
kohlerba 4:008e40a7d035 63 MAX30100_LED_CURR_11MA = 0x03,
kohlerba 4:008e40a7d035 64 MAX30100_LED_CURR_14_2MA = 0x04,
kohlerba 4:008e40a7d035 65 MAX30100_LED_CURR_17_4MA = 0x05,
kohlerba 4:008e40a7d035 66 MAX30100_LED_CURR_20_8MA = 0x06,
kohlerba 4:008e40a7d035 67 MAX30100_LED_CURR_24MA = 0x07,
kohlerba 4:008e40a7d035 68 MAX30100_LED_CURR_27_1MA = 0x08,
kohlerba 4:008e40a7d035 69 MAX30100_LED_CURR_30_6MA = 0x09,
kohlerba 4:008e40a7d035 70 MAX30100_LED_CURR_33_8MA = 0x0a,
kohlerba 4:008e40a7d035 71 MAX30100_LED_CURR_37MA = 0x0b,
kohlerba 4:008e40a7d035 72 MAX30100_LED_CURR_40_2MA = 0x0c,
kohlerba 4:008e40a7d035 73 MAX30100_LED_CURR_43_6MA = 0x0d,
kohlerba 4:008e40a7d035 74 MAX30100_LED_CURR_46_8MA = 0x0e,
kohlerba 4:008e40a7d035 75 MAX30100_LED_CURR_50MA = 0x0f
kohlerba 4:008e40a7d035 76 } LEDCurrent;
kohlerba 4:008e40a7d035 77
kohlerba 4:008e40a7d035 78 #define DEFAULT_MODE MAX30100_MODE_HRONLY
kohlerba 4:008e40a7d035 79 #define DEFAULT_SAMPLING_RATE MAX30100_SAMPRATE_100HZ
kohlerba 4:008e40a7d035 80 #define DEFAULT_PULSE_WIDTH MAX30100_SPC_PW_1600US_16BITS
kohlerba 4:008e40a7d035 81 #define DEFAULT_RED_LED_CURRENT MAX30100_LED_CURR_50MA
kohlerba 4:008e40a7d035 82 #define DEFAULT_IR_LED_CURRENT MAX30100_LED_CURR_50MA
kohlerba 4:008e40a7d035 83 #define EXPECTED_PART_ID 0x11
kohlerba 0:faf977308bdc 84
kohlerba 0:faf977308bdc 85 //Set up I2C, (SDA,SCL)
kohlerba 3:fa37b0c705b3 86 static I2C i2c(I2C_SDA, I2C_SCL);
kohlerba 3:fa37b0c705b3 87 static Serial pc(USBTX, USBRX); // tx, rx
kohlerba 0:faf977308bdc 88
kohlerba 0:faf977308bdc 89 class MAX30100 {
kohlerba 0:faf977308bdc 90
kohlerba 0:faf977308bdc 91 protected:
kohlerba 0:faf977308bdc 92
kohlerba 0:faf977308bdc 93 public:
kohlerba 0:faf977308bdc 94
kohlerba 4:008e40a7d035 95 //Variables
kohlerba 4:008e40a7d035 96 uint16_t rawIRValue;
kohlerba 4:008e40a7d035 97 uint16_t rawRedValue;
kohlerba 4:008e40a7d035 98
kohlerba 2:d329886938f1 99 //Wire read and write protocols
kohlerba 3:fa37b0c705b3 100 static int i2c_write (uint8_t i2c_addr, uint8_t register_addr, char* buffer, uint8_t Nbyte )
kohlerba 0:faf977308bdc 101 {
kohlerba 3:fa37b0c705b3 102 int ret;
kohlerba 3:fa37b0c705b3 103 char *tmp_buffer;
kohlerba 3:fa37b0c705b3 104
kohlerba 3:fa37b0c705b3 105 tmp_buffer = (char*)malloc(sizeof(char)*(Nbyte+1));
kohlerba 0:faf977308bdc 106
kohlerba 3:fa37b0c705b3 107 /* First, send device address. Then, send data and STOP condition */
kohlerba 3:fa37b0c705b3 108 tmp_buffer[0] = register_addr;
kohlerba 3:fa37b0c705b3 109 memcpy(tmp_buffer+1, buffer, Nbyte);
kohlerba 3:fa37b0c705b3 110
kohlerba 3:fa37b0c705b3 111 ret = i2c.write(i2c_addr, tmp_buffer, Nbyte+1, false);
kohlerba 3:fa37b0c705b3 112
kohlerba 3:fa37b0c705b3 113 return ret;
kohlerba 0:faf977308bdc 114 }
kohlerba 0:faf977308bdc 115
kohlerba 3:fa37b0c705b3 116 static int i2c_read (uint8_t i2c_addr, uint8_t register_addr, char* buffer, uint8_t Nbyte )
kohlerba 3:fa37b0c705b3 117 {
kohlerba 3:fa37b0c705b3 118 int ret;
kohlerba 3:fa37b0c705b3 119
kohlerba 3:fa37b0c705b3 120 /* Send device address, with no STOP condition */
kohlerba 3:fa37b0c705b3 121 ret = i2c.write(i2c_addr, (const char*)&register_addr, 1, true);
kohlerba 3:fa37b0c705b3 122 if(!ret) {
kohlerba 3:fa37b0c705b3 123 /* Read data, with STOP condition */
kohlerba 3:fa37b0c705b3 124 ret = i2c.read((i2c_addr|0x01), buffer, Nbyte, false);
kohlerba 0:faf977308bdc 125 }
kohlerba 3:fa37b0c705b3 126
kohlerba 3:fa37b0c705b3 127 return ret;
kohlerba 0:faf977308bdc 128 }
kohlerba 2:d329886938f1 129 //
kohlerba 0:faf977308bdc 130
kohlerba 3:fa37b0c705b3 131 uint8_t getRevID(void){
kohlerba 3:fa37b0c705b3 132 char buffer[1];
kohlerba 3:fa37b0c705b3 133 i2c_read(MAX30100_ADDRESS, MAX30100_REV_ID, &buffer[0], 1);
kohlerba 3:fa37b0c705b3 134 return buffer[0];
kohlerba 0:faf977308bdc 135 }
kohlerba 0:faf977308bdc 136
kohlerba 3:fa37b0c705b3 137 uint8_t getPartID(void){
kohlerba 3:fa37b0c705b3 138 char buffer[1];
kohlerba 3:fa37b0c705b3 139 i2c_read(MAX30100_ADDRESS, MAX30100_PART_ID, &buffer[0], 1);
kohlerba 3:fa37b0c705b3 140 return buffer[0];
kohlerba 0:faf977308bdc 141 }
kohlerba 0:faf977308bdc 142
kohlerba 4:008e40a7d035 143 void shutdown(void){
kohlerba 4:008e40a7d035 144 char reg[1];
kohlerba 4:008e40a7d035 145 i2c_read(MAX30100_ADDRESS, MAX30100_MODE_CONFIG, &reg[0], 1);
kohlerba 4:008e40a7d035 146 reg[0] = reg[0] | MAX30100_MC_SHDN;
kohlerba 4:008e40a7d035 147 i2c_write(MAX30100_ADDRESS, MAX30100_MODE_CONFIG, &reg[0], 1);
kohlerba 0:faf977308bdc 148 }
kohlerba 3:fa37b0c705b3 149
kohlerba 4:008e40a7d035 150 void resume(void){
kohlerba 4:008e40a7d035 151 char reg[1];
kohlerba 4:008e40a7d035 152 i2c_read(MAX30100_ADDRESS, MAX30100_MODE_CONFIG, &reg[0], 1);
kohlerba 4:008e40a7d035 153 reg[0] = reg[0] & ~MAX30100_MC_SHDN;
kohlerba 4:008e40a7d035 154 i2c_write(MAX30100_ADDRESS, MAX30100_MODE_CONFIG, &reg[0], 1);
kohlerba 4:008e40a7d035 155 }
kohlerba 4:008e40a7d035 156
kohlerba 4:008e40a7d035 157 void update(void){
kohlerba 4:008e40a7d035 158 readFifoData();
kohlerba 4:008e40a7d035 159 }
kohlerba 4:008e40a7d035 160
kohlerba 4:008e40a7d035 161 void printRegisters(void){
kohlerba 3:fa37b0c705b3 162 char reg[1];
kohlerba 3:fa37b0c705b3 163 i2c_read(MAX30100_ADDRESS, MAX30100_INT_STATUS, &reg[0], 1);
kohlerba 3:fa37b0c705b3 164 pc.printf("MAX30100_INT_STATUS: %d\r\n",reg[0]);
kohlerba 3:fa37b0c705b3 165 i2c_read(MAX30100_ADDRESS, MAX30100_INT_ENABLE, &reg[0], 1);
kohlerba 3:fa37b0c705b3 166 pc.printf("MAX30100_INT_ENABLE: %d\r\n",reg[0]);
kohlerba 3:fa37b0c705b3 167 i2c_read(MAX30100_ADDRESS, MAX30100_FIFO_WR_PTR, &reg[0], 1);
kohlerba 3:fa37b0c705b3 168 pc.printf("MAX30100_FIFO_WR_PTR: %d\r\n",reg[0]);
kohlerba 3:fa37b0c705b3 169 i2c_read(MAX30100_ADDRESS, MAX30100_OVRFLOW_CTR, &reg[0], 1);
kohlerba 3:fa37b0c705b3 170 pc.printf("MAX30100_OVRFLOW_CTR: %d\r\n",reg[0]);
kohlerba 3:fa37b0c705b3 171 i2c_read(MAX30100_ADDRESS, MAX30100_FIFO_RD_PTR, &reg[0], 1);
kohlerba 3:fa37b0c705b3 172 pc.printf("MAX30100_FIFO_RD_PTR: %d\r\n",reg[0]);
kohlerba 3:fa37b0c705b3 173 i2c_read(MAX30100_ADDRESS, MAX30100_FIFO_DATA, &reg[0], 1);
kohlerba 3:fa37b0c705b3 174 pc.printf("MAX30100_FIFO_DATA: %d\r\n",reg[0]);
kohlerba 3:fa37b0c705b3 175 i2c_read(MAX30100_ADDRESS, MAX30100_MODE_CONFIG, &reg[0], 1);
kohlerba 3:fa37b0c705b3 176 pc.printf("MAX30100_MODE_CONFIG: %d\r\n",reg[0]);
kohlerba 3:fa37b0c705b3 177 i2c_read(MAX30100_ADDRESS, MAX30100_SPO2_CONFIG, &reg[0], 1);
kohlerba 3:fa37b0c705b3 178 pc.printf("MAX30100_SPO2_CONFIG: %d\r\n",reg[0]);
kohlerba 3:fa37b0c705b3 179 i2c_read(MAX30100_ADDRESS, MAX30100_LED_CONFIG, &reg[0], 1);
kohlerba 3:fa37b0c705b3 180 pc.printf("MAX30100_LED_CONFIG: %d\r\n",reg[0]);
kohlerba 3:fa37b0c705b3 181 i2c_read(MAX30100_ADDRESS, MAX30100_TEMP_INTG, &reg[0], 1);
kohlerba 3:fa37b0c705b3 182 pc.printf("MAX30100_TEMP_INTG: %d\r\n",reg[0]);
kohlerba 3:fa37b0c705b3 183 i2c_read(MAX30100_ADDRESS, MAX30100_TEMP_FRAC, &reg[0], 1);
kohlerba 3:fa37b0c705b3 184 pc.printf("MAX30100_TEMP_FRAC: %d\r\n",reg[0]);
kohlerba 3:fa37b0c705b3 185 i2c_read(MAX30100_ADDRESS, MAX30100_REV_ID, &reg[0], 1);
kohlerba 3:fa37b0c705b3 186 pc.printf("MAX30100_REV_ID: %d\r\n",reg[0]);
kohlerba 3:fa37b0c705b3 187 i2c_read(MAX30100_ADDRESS, MAX30100_PART_ID, &reg[0], 1);
kohlerba 3:fa37b0c705b3 188 pc.printf("MAX30100_PART_ID: %d\r\n",reg[0]);
kohlerba 3:fa37b0c705b3 189 }
kohlerba 3:fa37b0c705b3 190
kohlerba 4:008e40a7d035 191 bool begin(){
kohlerba 4:008e40a7d035 192 if(getPartID() != POR_PART_ID){
kohlerba 4:008e40a7d035 193 return false;
kohlerba 4:008e40a7d035 194 }
kohlerba 4:008e40a7d035 195 setMode(DEFAULT_MODE);
kohlerba 4:008e40a7d035 196 setLedsPulseWidth(DEFAULT_PULSE_WIDTH);
kohlerba 4:008e40a7d035 197 setSamplingRate(DEFAULT_SAMPLING_RATE);
kohlerba 4:008e40a7d035 198 setLedsCurrent(DEFAULT_IR_LED_CURRENT, DEFAULT_RED_LED_CURRENT);
kohlerba 4:008e40a7d035 199 setHighresModeEnabled(true);
kohlerba 4:008e40a7d035 200
kohlerba 4:008e40a7d035 201 return true;
kohlerba 4:008e40a7d035 202 }
kohlerba 4:008e40a7d035 203
kohlerba 4:008e40a7d035 204 void setMode(Mode mode){
kohlerba 4:008e40a7d035 205 char reg[1];
kohlerba 4:008e40a7d035 206 reg[0] = mode;
kohlerba 4:008e40a7d035 207 i2c_write(MAX30100_ADDRESS, MAX30100_MODE_CONFIG, &reg[0], 1);
kohlerba 4:008e40a7d035 208 }
kohlerba 4:008e40a7d035 209
kohlerba 4:008e40a7d035 210 void setLedsPulseWidth(LEDPulseWidth ledPulseWidth){
kohlerba 4:008e40a7d035 211 char reg[1];
kohlerba 4:008e40a7d035 212 i2c_read(MAX30100_ADDRESS, MAX30100_SPO2_CONFIG, &reg[0], 1);
kohlerba 4:008e40a7d035 213 reg[0] = (reg[0] & 0xfc) | ledPulseWidth;
kohlerba 4:008e40a7d035 214 i2c_write(MAX30100_ADDRESS, MAX30100_SPO2_CONFIG, &reg[0], 1);
kohlerba 4:008e40a7d035 215 }
kohlerba 4:008e40a7d035 216
kohlerba 4:008e40a7d035 217 void setSamplingRate(SamplingRate samplingRate){
kohlerba 4:008e40a7d035 218 char reg[1];
kohlerba 4:008e40a7d035 219 i2c_read(MAX30100_ADDRESS, MAX30100_SPO2_CONFIG, &reg[0], 1);
kohlerba 4:008e40a7d035 220 reg[0] = (reg[0] & 0xe3) | (samplingRate << 2);
kohlerba 4:008e40a7d035 221 i2c_write(MAX30100_ADDRESS, MAX30100_SPO2_CONFIG, &reg[0], 1);
kohlerba 4:008e40a7d035 222 }
kohlerba 4:008e40a7d035 223
kohlerba 4:008e40a7d035 224 void setLedsCurrent(LEDCurrent irLedCurrent, LEDCurrent redLedCurrent){
kohlerba 4:008e40a7d035 225 char reg[1];
kohlerba 4:008e40a7d035 226 reg[0] = (redLedCurrent << 4) | irLedCurrent;
kohlerba 4:008e40a7d035 227 i2c_write(MAX30100_ADDRESS, MAX30100_LED_CONFIG, &reg[0], 1);
kohlerba 4:008e40a7d035 228 }
kohlerba 4:008e40a7d035 229
kohlerba 4:008e40a7d035 230 void setHighresModeEnabled(bool enabled){
kohlerba 4:008e40a7d035 231 char reg[1];
kohlerba 4:008e40a7d035 232 i2c_read(MAX30100_ADDRESS, MAX30100_SPO2_CONFIG, &reg[0], 1);
kohlerba 4:008e40a7d035 233 if(enabled){
kohlerba 4:008e40a7d035 234 reg[0] = reg[0] | MAX30100_SPC_SPO2_HI_RES_EN;
kohlerba 4:008e40a7d035 235 }
kohlerba 4:008e40a7d035 236 else{
kohlerba 4:008e40a7d035 237 reg[0] = reg[0] & ~MAX30100_SPC_SPO2_HI_RES_EN;
kohlerba 4:008e40a7d035 238 }
kohlerba 4:008e40a7d035 239 i2c_write(MAX30100_ADDRESS, MAX30100_SPO2_CONFIG, &reg[0], 1);
kohlerba 4:008e40a7d035 240 }
kohlerba 4:008e40a7d035 241
kohlerba 4:008e40a7d035 242 void readFifoData(void){
kohlerba 4:008e40a7d035 243 char reg[4];
kohlerba 4:008e40a7d035 244 i2c_read(MAX30100_ADDRESS, MAX30100_FIFO_DATA, &reg[0], 4);
kohlerba 4:008e40a7d035 245 rawIRValue = (reg[0] << 8) | reg[1];
kohlerba 4:008e40a7d035 246 rawRedValue = (reg[2] << 8) | reg[3];
kohlerba 4:008e40a7d035 247 }
kohlerba 4:008e40a7d035 248
kohlerba 4:008e40a7d035 249 void startTemperatureSampling(void){
kohlerba 4:008e40a7d035 250 char reg[1];
kohlerba 4:008e40a7d035 251 i2c_read(MAX30100_ADDRESS, MAX30100_MODE_CONFIG, &reg[0], 1);
kohlerba 4:008e40a7d035 252 reg[0] = reg[0] | MAX30100_MC_TEMP_EN;
kohlerba 4:008e40a7d035 253 i2c_write(MAX30100_ADDRESS, MAX30100_MODE_CONFIG, &reg[0], 1);
kohlerba 4:008e40a7d035 254 }
kohlerba 4:008e40a7d035 255
kohlerba 4:008e40a7d035 256 bool isTemperatureReady(void){
kohlerba 4:008e40a7d035 257 char reg[1];
kohlerba 4:008e40a7d035 258 bool ret;
kohlerba 4:008e40a7d035 259 ret = i2c_read(MAX30100_ADDRESS, MAX30100_MODE_CONFIG, &reg[0], 1);
kohlerba 4:008e40a7d035 260 ret = ret & MAX30100_MC_TEMP_EN;
kohlerba 4:008e40a7d035 261 return !ret;
kohlerba 4:008e40a7d035 262 }
kohlerba 4:008e40a7d035 263
kohlerba 4:008e40a7d035 264 float retrieveTemperature(void){
kohlerba 4:008e40a7d035 265 char reg[2];
kohlerba 4:008e40a7d035 266 i2c_read(MAX30100_ADDRESS, MAX30100_TEMP_INTG, &reg[0], 1);
kohlerba 4:008e40a7d035 267 i2c_read(MAX30100_ADDRESS, MAX30100_TEMP_FRAC, &reg[1], 1);
kohlerba 4:008e40a7d035 268 return reg[0] + (reg[1] * 0.0625);
kohlerba 4:008e40a7d035 269 }
kohlerba 4:008e40a7d035 270
kohlerba 1:0c2135629097 271 };
kohlerba 0:faf977308bdc 272 #endif