Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
| ||||||
| ||||||
| ||||||
Hand tools and fabrication machines | ||||||
| ||||||
| ||||||
|
Rural people of Bangladesh are suffering for a long time for a lack of good doctors and health checkups point. They need to travel a long way to take the health service and it is very difficult for old, child and ill people. Frequently it creates unexpected death. But leveraging IoT technology and advanced medical sensors the suffering of remote people can be reduced greatly.
Some health parameters like ECG, heart rate, body temperature, blood pressure, sleep quality, and SpO2 can give some important clues about the illness to a doctor. For the advancement of electronics and medical sensor technology, all those parameters can be measured easily by using low price sensors. And using the internet the information can be transferred to a doctor instantly and the doctor can easily identify the problem.
This demo device collects body temperature, oxygen saturation level, weight, and blood pressure and sends this parameter to AWS cloud. For collecting sensor data and sending it to AWS I used the AVR-IoT-WA development board.
HardwareFor measuring body temperature I used MLX90614 IR thermometer sensor, for oxygen saturation level I used Maxim MAX30102 pulse oximeter sensor. The weight and blood pressure are collecting from separate devices using Bluetooth.
I used HC-05 Bluetooth module for demonstration purposes. To display the recorded data and configure the device operation I used 0.96 inch OLED display.
MLX9014 thermometer, MAX30102 pulse oximeter and OLED display all are connected to the AVR-IoT board using I2C protocol. I develop the library for MPLAB X IDE for all three devices.
Bluetooth device communicate using serial port (TX, RX). The connection diagram was added in the schematic section.
For placing and making a portable health monitoring box I designed a 3D printed box and the design files are attached in CAD section.
A separate handheld unit was made for the thermometer. Because you need to keep it very close to your head while measuring the temperature.
The unit is connected to the main PCB using a cable.
AWS Data
#include "SSD1306.h"
#include "drivers/fonts.c"
#include "drivers/i2c_simple_master.h"
#include <string.h>
void OLED_Command(uint8_t temp)
{
i2c_write1ByteRegister(OLED_ADDRESS, 0x00, temp);
}
void OLED_Data(uint8_t temp)
{
i2c_write1ByteRegister(OLED_ADDRESS, 0x40, temp);
}
void OLED_init2()
{
OLED_Command(OLED_DISPLAYOFF); // 0xAE
OLED_Command(OLED_SETDISPLAYCLOCKDIV); // 0xD5
OLED_Command(0x80); // the suggested ratio 0x80
OLED_Command(OLED_SETMULTIPLEX); // 0xA8
OLED_Command(0x1F);
OLED_Command(OLED_SETDISPLAYOFFSET); // 0xD3
OLED_Command(0x0); // no offset
OLED_Command(OLED_SETSTARTLINE | 0x0); // line #0
OLED_Command(OLED_CHARGEPUMP); // 0x8D
OLED_Command(0xAF);
OLED_Command(OLED_MEMORYMODE); // 0x20
OLED_Command(0x00); // 0x0 act like ks0108
OLED_Command(OLED_SEGREMAP | 0x1);
OLED_Command(OLED_COMSCANDEC);
OLED_Command(OLED_SETCOMPINS); // 0xDA
OLED_Command(0x02);
OLED_Command(OLED_SETCONTRAST); // 0x81
OLED_Command(0x8F);
OLED_Command(OLED_SETPRECHARGE); // 0xd9
OLED_Command(0xF1);
OLED_Command(OLED_SETVCOMDETECT); // 0xDB
OLED_Command(0x40);
OLED_Command(OLED_DISPLAYALLON_RESUME); // 0xA4
OLED_Command(OLED_NORMALDISPLAY); // 0xA6
OLED_Command(OLED_DISPLAYON); //--turn on oled panel
}
//sets the X and Y coordinates
void OLED_YX(unsigned char Row, unsigned char Column)
{
OLED_Command( 0xB0 + Row);
OLED_Command( 0x00 + (8*Column & 0x0F) );
OLED_Command( 0x10 + ((8*Column>>4)&0x0F) );
}
//Writes a character to the OLED
void OLED_PutChar( char ch )
{
if ( ( ch < 32 ) || ( ch > 127 ) ){
ch = ' ';
}
const uint8_t *base = &OledFont[ch - 32][0];
uint8_t bytes[9];
bytes[0] = 0x40;
memmove( bytes + 1, base, 8 );
int i;
for (i = 1; i <= 8; i++){
i2c_write1ByteRegister(OLED_ADDRESS, 0x40, bytes[i]);
}
}
void OLED_Clear()
{
uint16_t row;
uint16_t col;
for (row = 0; row < 8; row++ ) {
for (col = 0; col < 16; col++ ) {
OLED_YX( row, col );
OLED_PutChar(' ');
}
}
}
void OLED_Write_String( char *s )
{
while (*s) OLED_PutChar( *s++);
}
void OLED_init()
{
OLED_write((Set_Display_ON_or_OFF_CMD | Display_OFF), CMD);
OLED_write(Set_Multiplex_Ratio_CMD, CMD);
OLED_write(0x3F, CMD);
OLED_write(Set_Display_Offset_CMD, CMD);
OLED_write(0x00, CMD);
OLED_write(Set_Display_Start_Line_CMD, CMD);
OLED_write((Set_Segment_Remap_CMD | Column_Address_0_Mapped_to_SEG127), CMD);
OLED_write((Set_COM_Output_Scan_Direction_CMD | Scan_from_COM63_to_0), CMD);
OLED_write(Set_Common_HW_Config_CMD, CMD);
OLED_write(0x12, CMD);
OLED_write(Set_Contrast_Control_CMD, CMD);
OLED_write(0xFF, CMD);
OLED_write(Set_Entire_Display_ON_CMD, CMD);
OLED_write(Set_Normal_or_Inverse_Display_CMD, CMD);
OLED_write(Set_Display_Clock_CMD, CMD);
OLED_write(0x80, CMD);
OLED_write(Set_Pre_charge_Period_CMD, CMD);
OLED_write(0x25, CMD);
OLED_write(Set_VCOMH_Level_CMD, CMD);
OLED_write(0x20, CMD);
OLED_write(Set_Page_Address_CMD, CMD);
OLED_write(0x00, CMD);
OLED_write(0x07, CMD);
OLED_write(Set_Page_Start_Address_CMD , CMD);
OLED_write(Set_Higher_Column_Start_Address_CMD, CMD);
OLED_write(Set_Lower_Column_Start_Address_CMD, CMD);
OLED_write(Set_Memory_Addressing_Mode_CMD, CMD);
OLED_write(0x02, CMD);
OLED_write(Set_Charge_Pump_CMD, CMD);
OLED_write(0x14, CMD);
OLED_write((Set_Display_ON_or_OFF_CMD | Display_ON), CMD);
}
void OLED_write(unsigned char value, unsigned char control_byte)
{
i2c_write1ByteRegister(OLED_ADDRESS, control_byte, value);
}
void OLED_gotoxy(unsigned char x_pos, unsigned char y_pos)
{
OLED_write((Set_Page_Start_Address_CMD + y_pos), CMD);
OLED_write(((x_pos & 0x0F) | Set_Lower_Column_Start_Address_CMD), CMD);
OLED_write((((x_pos & 0xF0) >> 0x04) | Set_Higher_Column_Start_Address_CMD), CMD);
}
void OLED_fill(unsigned char bmp_data)
{
unsigned char x_pos = 0x00;
unsigned char page = 0x00;
for(page = 0; page < y_max; page++)
{
OLED_gotoxy(x_min, page);
for(x_pos = x_min; x_pos < x_max; x_pos++)
{
i2c_write1ByteRegister(OLED_ADDRESS, DAT, bmp_data);
}
}
}
/*
void OLED_print_Image(const unsigned char *bmp, unsigned char pixel)
{
unsigned char x_pos = 0;
unsigned char page = 0;
if(pixel != 0)
{
pixel = 0xFF;
}
else
{
pixel = 0x00;
}
for(page = 0; page < y_max; page++)
{
OLED_gotoxy(x_min, page);
i2c_master_start();
i2c_master_send(SSD1306_I2C_Address);
i2c_master_send(DAT);
for(x_pos = x_min; x_pos < x_max; x_pos++)
{
i2c_master_send((*bmp++ ^ pixel));
}
i2c_master_stop();
}
}
*/
void OLED_clear_screen()
{
OLED_fill(0x00);
}
void OLED_clear_buffer()
{
unsigned int s = 0x0000;
for(s = 0; s < buffer_size; s++)
{
buffer[s] = 0x00;
}
}
void OLED_cursor(unsigned char x_pos, unsigned char y_pos)
{
unsigned char s = 0x00;
if(y_pos != 0x00)
{
if(x_pos == 1)
{
OLED_gotoxy(0x00, (y_pos + 0x02));
}
else
{
OLED_gotoxy((0x50 + ((x_pos - 0x02) * 0x06)), (y_pos + 0x02));
}
for(s = 0x00; s < 0x06; s++)
{
OLED_write(0xFF, DAT);
}
}
}
/*
void OLED_draw_bitmap(unsigned char xb, unsigned char yb, unsigned char xe, unsigned char ye, unsigned char *bmp_img)
{
unsigned int s = 0x0000;
unsigned char x_pos = 0x00;
unsigned char y_pos = 0x00;
for(y_pos = yb; y_pos <= ye; y_pos++)
{
OLED_gotoxy(xb, y_pos);
for(x_pos = xb; x_pos < xe; x_pos++)
{
OLED_write(bmp_img[s++], DAT);
}
}
}
*/
void OLED_print_char(unsigned char x_pos, unsigned char y_pos, unsigned char ch)
{
unsigned char chr = 0x00;
unsigned char s = 0x00;
chr = (ch - 0x20);
if(x_pos > (x_max - 0x06))
{
x_pos = 0x00;
y_pos++;
}
OLED_gotoxy(x_pos, y_pos);
for(s = 0x00; s < 0x06; s++)
{
OLED_write(font_regular[chr][s], DAT);
}
}
void OLED_print_string(unsigned char x_pos, unsigned char y_pos, unsigned char *ch)
{
unsigned char s = 0x00;
do
{
OLED_print_char(x_pos, y_pos, ch[s++]);
x_pos += 0x06;
}while((ch[s] >= 0x20) && (ch[s] <= 0x7F));
}
void OLED_print_chr(unsigned char x_pos, unsigned char y_pos, signed int value)
{
unsigned char ch = 0x00;
if(value < 0x00)
{
OLED_print_char(x_pos, y_pos, '-');
value = -value;
}
else
{
OLED_print_char(x_pos, y_pos,' ');
}
if((value > 99) && (value <= 999))
{
ch = (value / 100);
OLED_print_char((x_pos + 6), y_pos , (48 + ch));
ch = ((value % 100) / 10);
OLED_print_char((x_pos + 12), y_pos , (48 + ch));
ch = (value % 10);
OLED_print_char((x_pos + 18), y_pos , (48 + ch));
}
else if((value > 9) && (value <= 99))
{
ch = ((value % 100) / 10);
OLED_print_char((x_pos + 6), y_pos , (48 + ch));
ch = (value % 10);
OLED_print_char((x_pos + 12), y_pos , (48 + ch));
OLED_print_char((x_pos + 18), y_pos , 32);
}
else if((value >= 0) && (value <= 9))
{
ch = (value % 10);
OLED_print_char((x_pos + 6), y_pos , (48 + ch));
OLED_print_char((x_pos + 12), y_pos , 32);
OLED_print_char((x_pos + 18), y_pos , 32);
}
}
void OLED_print_int(unsigned char x_pos, unsigned char y_pos, signed long value)
{
unsigned char ch = 0x00;
if(value < 0)
{
OLED_print_char(x_pos, y_pos, '-');
value = -value;
}
else
{
OLED_print_char(x_pos, y_pos,' ');
}
if(value > 9999)
{
ch = (value / 10000);
OLED_print_char((x_pos + 6), y_pos , (48 + ch));
ch = ((value % 10000)/ 1000);
OLED_print_char((x_pos + 12), y_pos , (48 + ch));
ch = ((value % 1000) / 100);
OLED_print_char((x_pos + 18), y_pos , (48 + ch));
ch = ((value % 100) / 10);
OLED_print_char((x_pos + 24), y_pos , (48 + ch));
ch = (value % 10);
OLED_print_char((x_pos + 30), y_pos , (48 + ch));
}
else if((value > 999) && (value <= 9999))
{
ch = ((value % 10000)/ 1000);
OLED_print_char((x_pos + 6), y_pos , (48 + ch));
ch = ((value % 1000) / 100);
OLED_print_char((x_pos + 12), y_pos , (48 + ch));
ch = ((value % 100) / 10);
OLED_print_char((x_pos + 18), y_pos , (48 + ch));
ch = (value % 10);
OLED_print_char((x_pos + 24), y_pos , (48 + ch));
OLED_print_char((x_pos + 30), y_pos , 32);
}
else if((value > 99) && (value <= 999))
{
ch = ((value % 1000) / 100);
OLED_print_char((x_pos + 6), y_pos , (48 + ch));
ch = ((value % 100) / 10);
OLED_print_char((x_pos + 12), y_pos , (48 + ch));
ch = (value % 10);
OLED_print_char((x_pos + 18), y_pos , (48 + ch));
OLED_print_char((x_pos + 24), y_pos , 32);
OLED_print_char((x_pos + 30), y_pos , 32);
}
else if((value > 9) && (value <= 99))
{
ch = ((value % 100) / 10);
OLED_print_char((x_pos + 6), y_pos , (48 + ch));
ch = (value % 10);
OLED_print_char((x_pos + 12), y_pos , (48 + ch));
OLED_print_char((x_pos + 18), y_pos , 32);
OLED_print_char((x_pos + 24), y_pos , 32);
OLED_print_char((x_pos + 30), y_pos , 32);
}
else
{
ch = (value % 10);
OLED_print_char((x_pos + 6), y_pos , (48 + ch));
OLED_print_char((x_pos + 12), y_pos , 32);
OLED_print_char((x_pos + 18), y_pos , 32);
OLED_print_char((x_pos + 24), y_pos , 32);
OLED_print_char((x_pos + 30), y_pos , 32);
}
}
void OLED_print_decimal(unsigned char x_pos, unsigned char y_pos, unsigned int value, unsigned char points)
{
unsigned char ch = 0x00;
OLED_print_char(x_pos, y_pos, '.');
ch = (value / 1000);
OLED_print_char((x_pos + 6), y_pos , (48 + ch));
if(points > 1)
{
ch = ((value % 1000) / 100);
OLED_print_char((x_pos + 12), y_pos , (48 + ch));
if(points > 2)
{
ch = ((value % 100) / 10);
OLED_print_char((x_pos + 18), y_pos , (48 + ch));
if(points > 3)
{
ch = (value % 10);
OLED_print_char((x_pos + 24), y_pos , (48 + ch));
}
}
}
}
void OLED_print_float(unsigned char x_pos, unsigned char y_pos, float value, unsigned char points)
{
signed long tmp = 0x00;
tmp = value;
OLED_print_int(x_pos, y_pos, tmp);
tmp = ((value - tmp) * 10000);
if(tmp < 0)
{
tmp = -tmp;
}
if((value >= 9999) && (value < 99999))
{
OLED_print_decimal((x_pos + 36), y_pos, tmp, points);
}
else if((value >= 999) && (value < 9999))
{
OLED_print_decimal((x_pos + 30), y_pos, tmp, points);
}
else if((value >= 99) && (value < 999))
{
OLED_print_decimal((x_pos + 24), y_pos, tmp, points);
}
else if((value >= 9) && (value < 99))
{
OLED_print_decimal((x_pos + 18), y_pos, tmp, points);
}
else if(value < 9)
{
OLED_print_decimal((x_pos + 12), y_pos, tmp, points);
if((value) < 0)
{
OLED_print_char(x_pos, y_pos, '-');
}
else
{
OLED_print_char(x_pos, y_pos, ' ');
}
}
}
void Draw_Pixel(unsigned char x_pos, unsigned char y_pos, unsigned char colour)
{
unsigned char value = 0x00;
unsigned char page = 0x00;
unsigned char bit_pos = 0x00;
page = (y_pos / y_max);
bit_pos = (y_pos - (page * y_max));
value = buffer[((page * x_max) + x_pos)];
if((colour & YES) != NO)
{
value |= (1 << bit_pos);
}
else
{
value &= (~(1 << bit_pos));
}
buffer[((page * x_max) + x_pos)] = value;
OLED_gotoxy(x_pos, page);
OLED_write(value, DAT);
}
void Draw_Line(signed int x1, signed int y1, signed int x2, signed int y2, unsigned char colour)
{
signed int dx = 0x0000;
signed int dy = 0x0000;
signed int stepx = 0x0000;
signed int stepy = 0x0000;
signed int fraction = 0x0000;
dy = (y2 - y1);
dx = (x2 - x1);
if (dy < 0)
{
dy = -dy;
stepy = -1;
}
else
{
stepy = 1;
}
if (dx < 0)
{
dx = -dx;
stepx = -1;
}
else
{
stepx = 1;
}
dx <<= 1;
dy <<= 1;
Draw_Pixel(x1, y1, colour);
if(dx > dy)
{
fraction = (dy - (dx >> 1));
while (x1 != x2)
{
if(fraction >= 0)
{
y1 += stepy;
fraction -= dx;
}
x1 += stepx;
fraction += dy;
Draw_Pixel(x1, y1, colour);
}
}
else
{
fraction = (dx - (dy >> 1));
while (y1 != y2)
{
if (fraction >= 0)
{
x1 += stepx;
fraction -= dy;
}
y1 += stepy;
fraction += dx;
Draw_Pixel(x1, y1, colour);
}
}
}
void Draw_Rectangle(signed int x1, signed int y1, signed int x2, signed int y2, unsigned char fill, unsigned char colour, unsigned char type)
{
unsigned short i = 0x00;
unsigned short xmin = 0x00;
unsigned short xmax = 0x00;
unsigned short ymin = 0x00;
unsigned short ymax = 0x00;
if(fill != NO)
{
if(x1 < x2)
{
xmin = x1;
xmax = x2;
}
else
{
xmin = x2;
xmax = x1;
}
if(y1 < y2)
{
ymin = y1;
ymax = y2;
}
else
{
ymin = y2;
ymax = y1;
}
for(; xmin <= xmax; ++xmin)
{
for(i = ymin; i <= ymax; ++i)
{
Draw_Pixel(xmin, i, colour);
}
}
}
else
{
Draw_Line(x1, y1, x2, y1, colour);
Draw_Line(x1, y2, x2, y2, colour);
Draw_Line(x1, y1, x1, y2, colour);
Draw_Line(x2, y1, x2, y2, colour);
}
if(type != SQUARE)
{
Draw_Pixel(x1, y1, ~colour);
Draw_Pixel(x1, y2, ~colour);
Draw_Pixel(x2, y1, ~colour);
Draw_Pixel(x2, y2, ~colour);
}
}
void Draw_Circle(signed int xc, signed int yc, signed int radius, unsigned char fill, unsigned char colour)
{
signed int a = 0x0000;
signed int b = 0x0000;
signed int P = 0x0000;
b = radius;
P = (1 - b);
do
{
if(fill != NO)
{
Draw_Line((xc - a), (yc + b), (xc + a), (yc + b), colour);
Draw_Line((xc - a), (yc - b), (xc + a), (yc - b), colour);
Draw_Line((xc - b), (yc + a), (xc + b), (yc + a), colour);
Draw_Line((xc - b), (yc - a), (xc + b), (yc - a), colour);
}
else
{
Draw_Pixel((xc + a), (yc + b), colour);
Draw_Pixel((xc + b), (yc + a), colour);
Draw_Pixel((xc - a), (yc + b), colour);
Draw_Pixel((xc - b), (yc + a), colour);
Draw_Pixel((xc + b), (yc - a), colour);
Draw_Pixel((xc + a), (yc - b), colour);
Draw_Pixel((xc - a), (yc - b), colour);
Draw_Pixel((xc - b), (yc - a), colour);
}
if(P < 0)
{
P += (3 + (2 * a++));
}
else
{
P += (5 + (2 * ((a++) - (b--))));
}
}while(a <= b);
}
/*
* File: algorithm.c
* Author: MKA
*
* Created on December 30, 2020, 6:17 AM
*/
/** \file algorithm.cpp ******************************************************
*
* Project: MAXREFDES117#
* Filename: algorithm.cpp
* Description: This module calculates the heart rate/SpO2 level
*
*
* --------------------------------------------------------------------
*
* This code follows the following naming conventions:
*
* char ch_pmod_value
* char (array) s_pmod_s_string[16]
* float f_pmod_value
* int32_t n_pmod_value
* int32_t (array) an_pmod_value[16]
* int16_t w_pmod_value
* int16_t (array) aw_pmod_value[16]
* uint16_t uw_pmod_value
* uint16_t (array) auw_pmod_value[16]
* uint8_t uch_pmod_value
* uint8_t (array) auch_pmod_buffer[16]
* uint32_t un_pmod_value
* int32_t * pn_pmod_value
*
* ------------------------------------------------------------------------- */
/*******************************************************************************
* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************
*/
#define FreqS 25 //sampling frequency
#define BUFFER_SIZE (FreqS * 4)
#define MA4_SIZE 4 // DONOT CHANGE
#define min(x,y) ((x) < (y) ? (x) : (y)) //Defined in Arduino.h
//uch_spo2_table is approximated as -45.060*ratioAverage* ratioAverage + 30.354 *ratioAverage + 94.845 ;
const uint8_t uch_spo2_table[184]={ 95, 95, 95, 96, 96, 96, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 99, 99, 99, 99,
99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 97, 97,
97, 97, 96, 96, 96, 96, 95, 95, 95, 94, 94, 94, 93, 93, 93, 92, 92, 92, 91, 91,
90, 90, 89, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 82, 82, 81, 81,
80, 80, 79, 78, 78, 77, 76, 76, 75, 74, 74, 73, 72, 72, 71, 70, 69, 69, 68, 67,
66, 66, 65, 64, 63, 62, 62, 61, 60, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50,
49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 31, 30, 29,
28, 27, 26, 25, 23, 22, 21, 20, 19, 17, 16, 15, 14, 12, 11, 10, 9, 7, 6, 5,
3, 2, 1 } ;
static int32_t an_x[ BUFFER_SIZE]; //ir
static int32_t an_y[ BUFFER_SIZE]; //red
//void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer, int32_t n_ir_buffer_length, uint32_t *pun_red_buffer, int32_t *pn_spo2, int8_t *pch_spo2_valid, int32_t *pn_heart_rate, int8_t *pch_hr_valid);
void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer, int32_t n_ir_buffer_length, uint32_t *pun_red_buffer);
void maxim_find_peaks(int32_t *pn_locs, int32_t *n_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height, int32_t n_min_distance, int32_t n_max_num);
void maxim_peaks_above_min_height(int32_t *pn_locs, int32_t *n_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height);
void maxim_remove_close_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_min_distance);
void maxim_sort_ascend(int32_t *pn_x, int32_t n_size);
void maxim_sort_indices_descend(int32_t *pn_x, int32_t *pn_indx, int32_t n_size);
void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer, int32_t n_ir_buffer_length, uint32_t *pun_red_buffer)
/**
* \brief Calculate the heart rate and SpO2 level
* \par Details
* By detecting peaks of PPG cycle and corresponding AC/DC of red/infra-red signal, the an_ratio for the SPO2 is computed.
* Since this algorithm is aiming for Arm M0/M3. formaula for SPO2 did not achieve the accuracy due to register overflow.
* Thus, accurate SPO2 is precalculated and save longo uch_spo2_table[] per each an_ratio.
*
* \param[in] *pun_ir_buffer - IR sensor data buffer
* \param[in] n_ir_buffer_length - IR sensor data buffer length
* \param[in] *pun_red_buffer - Red sensor data buffer
* \param[out] *pn_spo2 - Calculated SpO2 value
* \param[out] *pch_spo2_valid - 1 if the calculated SpO2 value is valid
* \param[out] *pn_heart_rate - Calculated heart rate value
* \param[out] *pch_hr_valid - 1 if the calculated heart rate value is valid
*
* \retval None
*/
{
int32_t *pn_spo2;
int8_t *pch_spo2_valid;
int32_t *pn_heart_rate;
int8_t *pch_hr_valid;
uint32_t un_ir_mean;
int32_t k, n_i_ratio_count;
int32_t i, n_exact_ir_valley_locs_count, n_middle_idx;
int32_t n_th1, n_npks;
int32_t an_ir_valley_locs[15] ;
int32_t n_peak_interval_sum;
int32_t n_y_ac, n_x_ac;
int32_t n_spo2_calc;
int32_t n_y_dc_max, n_x_dc_max;
int32_t n_y_dc_max_idx = 0;
int32_t n_x_dc_max_idx = 0;
int32_t an_ratio[5], n_ratio_average;
int32_t n_nume, n_denom ;
// calculates DC mean and subtract DC from ir
un_ir_mean =0;
for (k=0 ; k<n_ir_buffer_length ; k++ ) un_ir_mean += pun_ir_buffer[k] ;
un_ir_mean =un_ir_mean/n_ir_buffer_length ;
// remove DC and invert signal so that we can use peak detector as valley detector
for (k=0 ; k<n_ir_buffer_length ; k++ )
an_x[k] = -1*(pun_ir_buffer[k] - un_ir_mean) ;
// 4 pt Moving Average
for(k=0; k< BUFFER_SIZE-MA4_SIZE; k++){
an_x[k]=( an_x[k]+an_x[k+1]+ an_x[k+2]+ an_x[k+3])/(int32_t)4;
}
// calculate threshold
n_th1=0;
for ( k=0 ; k<BUFFER_SIZE ;k++){
n_th1 += an_x[k];
}
n_th1= n_th1/ ( BUFFER_SIZE);
if( n_th1<30) n_th1=30; // min allowed
if( n_th1>60) n_th1=60; // max allowed
for ( k=0 ; k<15;k++) an_ir_valley_locs[k]=0;
// since we flipped signal, we use peak detector as valley detector
maxim_find_peaks( an_ir_valley_locs, &n_npks, an_x, BUFFER_SIZE, n_th1, 4, 15 );//peak_height, peak_distance, max_num_peaks
n_peak_interval_sum =0;
if (n_npks>=2){
for (k=1; k<n_npks; k++) n_peak_interval_sum += (an_ir_valley_locs[k] -an_ir_valley_locs[k -1] ) ;
n_peak_interval_sum =n_peak_interval_sum/(n_npks-1);
*pn_heart_rate =(int32_t)( (FreqS*60)/ n_peak_interval_sum );
*pch_hr_valid = 1;
}
else {
*pn_heart_rate = -999; // unable to calculate because # of peaks are too small
*pch_hr_valid = 0;
}
// load raw value again for SPO2 calculation : RED(=y) and IR(=X)
for (k=0 ; k<n_ir_buffer_length ; k++ ) {
an_x[k] = pun_ir_buffer[k] ;
an_y[k] = pun_red_buffer[k] ;
}
// find precise min near an_ir_valley_locs
n_exact_ir_valley_locs_count =n_npks;
//using exact_ir_valley_locs , find ir-red DC andir-red AC for SPO2 calibration an_ratio
//finding AC/DC maximum of raw
n_ratio_average =0;
n_i_ratio_count = 0;
for(k=0; k< 5; k++) an_ratio[k]=0;
for (k=0; k< n_exact_ir_valley_locs_count; k++){
if (an_ir_valley_locs[k] > BUFFER_SIZE ){
*pn_spo2 = -999 ; // do not use SPO2 since valley loc is out of range
*pch_spo2_valid = 0;
return;
}
}
// find max between two valley locations
// and use an_ratio betwen AC compoent of Ir & Red and DC compoent of Ir & Red for SPO2
for (k=0; k< n_exact_ir_valley_locs_count-1; k++){
n_y_dc_max= -16777216 ;
n_x_dc_max= -16777216;
if (an_ir_valley_locs[k+1]-an_ir_valley_locs[k] >3){
for (i=an_ir_valley_locs[k]; i< an_ir_valley_locs[k+1]; i++){
if (an_x[i]> n_x_dc_max) {n_x_dc_max =an_x[i]; n_x_dc_max_idx=i;}
if (an_y[i]> n_y_dc_max) {n_y_dc_max =an_y[i]; n_y_dc_max_idx=i;}
}
n_y_ac= (an_y[an_ir_valley_locs[k+1]] - an_y[an_ir_valley_locs[k] ] )*(n_y_dc_max_idx -an_ir_valley_locs[k]); //red
n_y_ac= an_y[an_ir_valley_locs[k]] + n_y_ac/ (an_ir_valley_locs[k+1] - an_ir_valley_locs[k]) ;
n_y_ac= an_y[n_y_dc_max_idx] - n_y_ac; // subracting linear DC compoenents from raw
n_x_ac= (an_x[an_ir_valley_locs[k+1]] - an_x[an_ir_valley_locs[k] ] )*(n_x_dc_max_idx -an_ir_valley_locs[k]); // ir
n_x_ac= an_x[an_ir_valley_locs[k]] + n_x_ac/ (an_ir_valley_locs[k+1] - an_ir_valley_locs[k]);
n_x_ac= an_x[n_y_dc_max_idx] - n_x_ac; // subracting linear DC compoenents from raw
n_nume=( n_y_ac *n_x_dc_max)>>7 ; //prepare X100 to preserve floating value
n_denom= ( n_x_ac *n_y_dc_max)>>7;
if (n_denom>0 && n_i_ratio_count <5 && n_nume != 0)
{
an_ratio[n_i_ratio_count]= (n_nume*100)/n_denom ; //formular is ( n_y_ac *n_x_dc_max) / ( n_x_ac *n_y_dc_max) ;
n_i_ratio_count++;
}
}
}
// choose median value since PPG signal may varies from beat to beat
maxim_sort_ascend(an_ratio, n_i_ratio_count);
n_middle_idx= n_i_ratio_count/2;
if (n_middle_idx >1)
n_ratio_average =( an_ratio[n_middle_idx-1] +an_ratio[n_middle_idx])/2; // use median
else
n_ratio_average = an_ratio[n_middle_idx ];
if( n_ratio_average>2 && n_ratio_average <184){
n_spo2_calc= uch_spo2_table[n_ratio_average] ;
*pn_spo2 = n_spo2_calc ;
*pch_spo2_valid = 1;// float_SPO2 = -45.060*n_ratio_average* n_ratio_average/10000 + 30.354 *n_ratio_average/100 + 94.845 ; // for comparison with table
}
else{
*pn_spo2 = -999 ; // do not use SPO2 since signal an_ratio is out of range
*pch_spo2_valid = 0;
}
}
void maxim_find_peaks( int32_t *pn_locs, int32_t *n_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height, int32_t n_min_distance, int32_t n_max_num )
/**
* \brief Find peaks
* \par Details
* Find at most MAX_NUM peaks above MIN_HEIGHT separated by at least MIN_DISTANCE
*
* \retval None
*/
{
maxim_peaks_above_min_height( pn_locs, n_npks, pn_x, n_size, n_min_height );
maxim_remove_close_peaks( pn_locs, n_npks, pn_x, n_min_distance );
*n_npks = min( *n_npks, n_max_num );
}
void maxim_peaks_above_min_height( int32_t *pn_locs, int32_t *n_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height )
/**
* \brief Find peaks above n_min_height
* \par Details
* Find all peaks above MIN_HEIGHT
*
* \retval None
*/
{
int32_t i = 1, n_width;
*n_npks = 0;
while (i < n_size-1){
if (pn_x[i] > n_min_height && pn_x[i] > pn_x[i-1]){ // find left edge of potential peaks
n_width = 1;
while (i+n_width < n_size && pn_x[i] == pn_x[i+n_width]) // find flat peaks
n_width++;
if (pn_x[i] > pn_x[i+n_width] && (*n_npks) < 15 ){ // find right edge of peaks
pn_locs[(*n_npks)++] = i;
// for flat peaks, peak location is left edge
i += n_width+1;
}
else
i += n_width;
}
else
i++;
}
}
void maxim_remove_close_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_min_distance)
/**
* \brief Remove peaks
* \par Details
* Remove peaks separated by less than MIN_DISTANCE
*
* \retval None
*/
{
int32_t i, j, n_old_npks, n_dist;
/* Order peaks from large to small */
maxim_sort_indices_descend( pn_x, pn_locs, *pn_npks );
for ( i = -1; i < *pn_npks; i++ ){
n_old_npks = *pn_npks;
*pn_npks = i+1;
for ( j = i+1; j < n_old_npks; j++ ){
n_dist = pn_locs[j] - ( i == -1 ? -1 : pn_locs[i] ); // lag-zero peak of autocorr is at index -1
if ( n_dist > n_min_distance || n_dist < -n_min_distance )
pn_locs[(*pn_npks)++] = pn_locs[j];
}
}
// Resort indices int32_to ascending order
maxim_sort_ascend( pn_locs, *pn_npks );
}
void maxim_sort_ascend(int32_t *pn_x, int32_t n_size)
/**
* \brief Sort array
* \par Details
* Sort array in ascending order (insertion sort algorithm)
*
* \retval None
*/
{
int32_t i, j, n_temp;
for (i = 1; i < n_size; i++) {
n_temp = pn_x[i];
for (j = i; j > 0 && n_temp < pn_x[j-1]; j--)
pn_x[j] = pn_x[j-1];
pn_x[j] = n_temp;
}
}
void maxim_sort_indices_descend( int32_t *pn_x, int32_t *pn_indx, int32_t n_size)
/**
* \brief Sort indices
* \par Details
* Sort indices according to descending order (insertion sort algorithm)
*
* \retval None
*/
{
int32_t i, j, n_temp;
for (i = 1; i < n_size; i++) {
n_temp = pn_indx[i];
for (j = i; j > 0 && pn_x[n_temp] > pn_x[pn_indx[j-1]]; j--)
pn_indx[j] = pn_indx[j-1];
pn_indx[j] = n_temp;
}
}
#ifndef MAX30102_H_
#define MAX30102_H_
#define MAX30102_IIC_ADDRESS 0x57 //I2C Address
// MAX30102 Commands
// Interrupt configuration (pg 13, 14)
//#define MAX30102_INT_A_FULL_MASK (byte)~0b10000000
static const uint8_t MAX30102_INT_A_FULL_MASK = (uint8_t)~0b10000000;
#define MAX30102_INT_A_FULL_ENABLE 0x80
#define MAX30102_INT_A_FULL_DISABLE 0x00
//#define MAX30102_INT_DATA_RDY_MASK (byte)~0b01000000
static const uint8_t MAX30102_INT_DATA_RDY_MASK = (uint8_t)~0b01000000;
#define MAX30102_INT_DATA_RDY_ENABLE 0x40
#define MAX30102_INT_DATA_RDY_DISABLE 0x00
//#define MAX30102_INT_ALC_OVF_MASK (byte)~0b00100000
static const uint8_t MAX30102_INT_ALC_OVF_MASK = (uint8_t)~0b00100000;
#define MAX30102_INT_ALC_OVF_ENABLE 0x20
#define MAX30102_INT_ALC_OVF_DISABLE 0x00
//#define MAX30102_INT_PROX_INT_MASK (byte)~0b00010000
static const uint8_t MAX30102_INT_PROX_INT_MASK = (uint8_t)~0b00010000;
#define MAX30102_INT_PROX_INT_ENABLE 0x10
#define MAX30102_INT_PROX_INT_DISABLE 0x00
//#define MAX30102_INT_DIE_TEMP_RDY_MASK (byte)~0b00000010
static const uint8_t MAX30102_INT_DIE_TEMP_RDY_MASK = (uint8_t)~0b00000010;
#define MAX30102_INT_DIE_TEMP_RDY_ENABLE 0x02
#define MAX30102_INT_DIE_TEMP_RDY_DISABLE 0x00
// Mode configuration commands (page 19)
#define MAX30102_SHUTDOWN_MASK 0x7F
#define MAX30102_SHUTDOWN 0x80
#define MAX30102_WAKEUP 0x00
#define MAX30102_RESET_MASK 0xBF
#define MAX30102_RESET 0x40
#define MAX30102_MODE_MASK 0xF8
#define MAX30102_MODE_REDONLY 0x02
#define MAX30102_MODE_REDIRONLY 0x03
#define MAX30102_MODE_MULTILED 0x07
// Particle sensing configuration commands (pgs 19-20)
#define MAX30102_ADCRANGE_MASK 0x9F
#define MAX30102_ADCRANGE_2048 0x00
#define MAX30102_ADCRANGE_4096 0x20
#define MAX30102_ADCRANGE_8192 0x40
#define MAX30102_ADCRANGE_16384 0x60
#define MAX30102_SAMPLERATE_MASK 0xE3
#define MAX30102_SAMPLERATE_50 0x00
#define MAX30102_SAMPLERATE_100 0x04
#define MAX30102_SAMPLERATE_200 0x08
#define MAX30102_SAMPLERATE_400 0x0C
#define MAX30102_SAMPLERATE_800 0x10
#define MAX30102_SAMPLERATE_1000 0x14
#define MAX30102_SAMPLERATE_1600 0x18
#define MAX30102_SAMPLERATE_3200 0x1C
#define MAX30102_PULSEWIDTH_MASK 0xFC
#define MAX30102_PULSEWIDTH_69 0x00
#define MAX30102_PULSEWIDTH_118 0x01
#define MAX30102_PULSEWIDTH_215 0x02
#define MAX30102_PULSEWIDTH_411 0x03
// Configuration Registers
#define MAX30102_FIFOCONFIG 0x08
#define MAX30102_MODECONFIG 0x09
#define MAX30102_PARTICLECONFIG 0x0A // Note, sometimes listed as "SPO2" config in datasheet (pg. 11)
#define MAX30102_LED1_PULSEAMP 0x0C
#define MAX30102_LED2_PULSEAMP 0x0D
#define MAX30102_MULTILEDCONFIG1 0x11
#define MAX30102_MULTILEDCONFIG2 0x12
// FIFO Registers
#define MAX30102_FIFOWRITEPTR 0x04
#define MAX30102_FIFOOVERFLOW 0x05
#define MAX30102_FIFOREADPTR 0x06
#define MAX30102_FIFODATA 0x07
// Status Registers
#define MAX30102_INTSTAT1 0x00
#define MAX30102_INTSTAT2 0x01
#define MAX30102_INTENABLE1 0x02
#define MAX30102_INTENABLE2 0x03
// Die Temperature Registers
#define MAX30102_DIETEMPINT 0x1F
#define MAX30102_DIETEMPFRAC 0x20
#define MAX30102_DIETEMPCONFIG 0x21
// Part ID Registers
#define MAX30102_REVISIONID 0xFE
#define MAX30102_PARTID 0xFF
// Proximity Function Registers
#define MAX30102_PROXINTTHRESH 0x30
//Multi-LED Mode configuration (pg 22)
#define MAX30102_SLOT1_MASK 0xF8
#define MAX30102_SLOT2_MASK 0x8F
#define MAX30102_SLOT3_MASK 0xF8
#define MAX30102_SLOT4_MASK 0x8F
#define SLOT_NONE 0x00
#define SLOT_RED_LED 0x01
#define SLOT_IR_LED 0x02
#define SLOT_GREEN_LED 0x03
#define SLOT_NONE_PILOT 0x04
#define SLOT_RED_PILOT 0x05
#define SLOT_IR_PILOT 0x06
#define SLOT_GREEN_PILOT 0x07
//#define MAX30102_SAMPLEAVG_MASK (byte)~0b11100000
static const uint8_t MAX30102_SAMPLEAVG_MASK = (uint8_t)~0b11100000;
#define MAX30102_SAMPLEAVG_1 0x00
#define MAX30102_SAMPLEAVG_2 0x20
#define MAX30102_SAMPLEAVG_4 0x40
#define MAX30102_SAMPLEAVG_8 0x60
#define MAX30102_SAMPLEAVG_16 0x80
#define MAX30102_SAMPLEAVG_32 0xA0
#define MAX30102_ROLLOVER_MASK 0xEF
#define MAX30102_ROLLOVER_ENABLE 0x10
#define MAX30102_ROLLOVER_DISABLE 0x00
#define MAX30102_A_FULL_MASK 0xF0
#define MAX30102_EXPECTED_PARTID 0x15
#define MAX30102_SENSE_BUF_SIZE 32
#define I2C_BUFFER_LENGTH 32
//Configuration Options
//FIFO Configuration(Register address 0x08)
//sampleAverage(Table 3. Sample Averaging)
#define SAMPLEAVG_1 0
#define SAMPLEAVG_2 1
#define SAMPLEAVG_4 2
#define SAMPLEAVG_8 3
#define SAMPLEAVG_16 4
#define SAMPLEAVG_32 5
//Mode configuration(Register address 0x09)
//ledMode(Table 4. Mode Control)
#define MODE_REDONLY 2
#define MODE_RED_IR 3
#define MODE_MULTILED 7
//Particle sensing configuration(Register address 0x0A)
//adcRange(Table 5. SpO2 ADC Range Control)
#define ADCRANGE_2048 0
#define ADCRANGE_4096 1
#define ADCRANGE_8192 2
#define ADCRANGE_16384 3
//sampleRate(Table 6. SpO2 Sample Rate Control)
#define SAMPLERATE_50 0
#define SAMPLERATE_100 1
#define SAMPLERATE_200 2
#define SAMPLERATE_400 3
#define SAMPLERATE_800 4
#define SAMPLERATE_1000 5
#define SAMPLERATE_1600 6
#define SAMPLERATE_3200 7
//pulseWidth(Table 7. LED Pulse Width Control)
#define PULSEWIDTH_69 0
#define PULSEWIDTH_118 1
#define PULSEWIDTH_215 2
#define PULSEWIDTH_411 3
//Multi-LED Mode Control Registers(Register address 0x011)
//#define SLOT_NONE 0
//#define SLOT_RED_LED 1
//#define SLOT_IR_LED 2
bool MAX30102_begin();
void MAX30102_enableAlmostFull(void);
void MAX30102_disableAlmostFull(void);
void MAX30102_enableDataReady(void);
void MAX30102_disableDataReady(void);
void MAX30102_enableALCOverflow(void);
void MAX30102_disableALCOverflow(void);
void MAX30102_enablePROXINT(void);
void MAX30102_disablePROXINT(void);
void MAX30102_enableDieTempReady(void);
void MAX30102_disableDieTempReady(void);
void MAX30102_enableFIFORollover(void);
void MAX30102_disableFIFORollover(void);
void MAX30102_setFIFOAlmostFull(uint8_t numberOfSamples);
void MAX30102_setFIFOAverage(uint8_t numberOfSamples);
void MAX30102_shutDown(void);
void MAX30102_wakeUp(void);
void MAX30102_setLEDMode(uint8_t mode);
void MAX30102_setADCRange(uint8_t adcRange);
void MAX30102_setSampleRate(uint8_t sampleRate);
void MAX30102_setPulseWidth(uint8_t pulseWidth);
void MAX30102_setPulseAmplitudeRed(uint8_t amplitude);
void MAX30102_setPulseAmplitudeIR(uint8_t amplitude);
void MAX30102_disableAllSlots(void);
uint8_t MAX30102_getWritePointer(void);
uint8_t MAX30102_getReadPointer(void);
uint8_t MAX30102_getPartID(void);
uint8_t MAX30102_getRevisionID(void);
void MAX30102_resetFIFO(void);
void MAX30102_softReset(void);
void MAX30102_enableSlot(uint8_t slotNumber, uint8_t device);
float MAX30102_readTemperatureC(void);
float MAX30102_readTemperatureF(void);
void MAX30102_sensorConfiguration(uint8_t ledBrightness, uint8_t sampleAverage, uint8_t ledMode, uint32_t sampleRate, uint32_t pulseWidth, uint32_t adcRange);
uint32_t MAX30102_getRed(void);
uint32_t MAX30102_getIR(void);
uint16_t MAX30102_getNewData(void);
void bitMask(uint8_t reg, uint8_t mask, uint8_t thing);
uint8_t MAX30102_available(void);
void MAX30102_nextSample(void);
void MAX30102_heartrateAndOxygenSaturation(int32_t* SPO2, int8_t* SPO2Valid, int32_t* heartRate, int8_t* heartRateValid);
#endif /* MAX30102_H_ */
/*
* File: MAX30102.c
* Author: Md. Khairul Alam
*
* Created on December 29, 2020, 3:30 PM
*/
#include "MAX30102.h"
#include <string.h>
#include <avr/io.h>
#include "drivers/i2c_simple_master.h"
#include "time_service.h"
#include "debug_print.h"
#define MAX30102_IIC_ADDRESS 0x57 //I2C Address
// MAX30102 Commands
// Interrupt configuration (pg 13, 14)
//#define MAX30102_INT_A_FULL_MASK (byte)~0b10000000
static const uint8_t MAX30102_INT_A_FULL_MASK = (uint8_t)~0b10000000;
#define MAX30102_INT_A_FULL_ENABLE 0x80
#define MAX30102_INT_A_FULL_DISABLE 0x00
//#define MAX30102_INT_DATA_RDY_MASK (byte)~0b01000000
static const uint8_t MAX30102_INT_DATA_RDY_MASK = (uint8_t)~0b01000000;
#define MAX30102_INT_DATA_RDY_ENABLE 0x40
#define MAX30102_INT_DATA_RDY_DISABLE 0x00
//#define MAX30102_INT_ALC_OVF_MASK (byte)~0b00100000
static const uint8_t MAX30102_INT_ALC_OVF_MASK = (uint8_t)~0b00100000;
#define MAX30102_INT_ALC_OVF_ENABLE 0x20
#define MAX30102_INT_ALC_OVF_DISABLE 0x00
//#define MAX30102_INT_PROX_INT_MASK (byte)~0b00010000
static const uint8_t MAX30102_INT_PROX_INT_MASK = (uint8_t)~0b00010000;
#define MAX30102_INT_PROX_INT_ENABLE 0x10
#define MAX30102_INT_PROX_INT_DISABLE 0x00
//#define MAX30102_INT_DIE_TEMP_RDY_MASK (byte)~0b00000010
static const uint8_t MAX30102_INT_DIE_TEMP_RDY_MASK = (uint8_t)~0b00000010;
#define MAX30102_INT_DIE_TEMP_RDY_ENABLE 0x02
#define MAX30102_INT_DIE_TEMP_RDY_DISABLE 0x00
// Mode configuration commands (page 19)
#define MAX30102_SHUTDOWN_MASK 0x7F
#define MAX30102_SHUTDOWN 0x80
#define MAX30102_WAKEUP 0x00
#define MAX30102_RESET_MASK 0xBF
#define MAX30102_RESET 0x40
#define MAX30102_MODE_MASK 0xF8
#define MAX30102_MODE_REDONLY 0x02
#define MAX30102_MODE_REDIRONLY 0x03
#define MAX30102_MODE_MULTILED 0x07
// Particle sensing configuration commands (pgs 19-20)
#define MAX30102_ADCRANGE_MASK 0x9F
#define MAX30102_ADCRANGE_2048 0x00
#define MAX30102_ADCRANGE_4096 0x20
#define MAX30102_ADCRANGE_8192 0x40
#define MAX30102_ADCRANGE_16384 0x60
#define MAX30102_SAMPLERATE_MASK 0xE3
#define MAX30102_SAMPLERATE_50 0x00
#define MAX30102_SAMPLERATE_100 0x04
#define MAX30102_SAMPLERATE_200 0x08
#define MAX30102_SAMPLERATE_400 0x0C
#define MAX30102_SAMPLERATE_800 0x10
#define MAX30102_SAMPLERATE_1000 0x14
#define MAX30102_SAMPLERATE_1600 0x18
#define MAX30102_SAMPLERATE_3200 0x1C
#define MAX30102_PULSEWIDTH_MASK 0xFC
#define MAX30102_PULSEWIDTH_69 0x00
#define MAX30102_PULSEWIDTH_118 0x01
#define MAX30102_PULSEWIDTH_215 0x02
#define MAX30102_PULSEWIDTH_411 0x03
// Configuration Registers
#define MAX30102_FIFOCONFIG 0x08
#define MAX30102_MODECONFIG 0x09
#define MAX30102_PARTICLECONFIG 0x0A // Note, sometimes listed as "SPO2" config in datasheet (pg. 11)
#define MAX30102_LED1_PULSEAMP 0x0C
#define MAX30102_LED2_PULSEAMP 0x0D
#define MAX30102_MULTILEDCONFIG1 0x11
#define MAX30102_MULTILEDCONFIG2 0x12
// FIFO Registers
#define MAX30102_FIFOWRITEPTR 0x04
#define MAX30102_FIFOOVERFLOW 0x05
#define MAX30102_FIFOREADPTR 0x06
#define MAX30102_FIFODATA 0x07
// Status Registers
#define MAX30102_INTSTAT1 0x00
#define MAX30102_INTSTAT2 0x01
#define MAX30102_INTENABLE1 0x02
#define MAX30102_INTENABLE2 0x03
// Die Temperature Registers
#define MAX30102_DIETEMPINT 0x1F
#define MAX30102_DIETEMPFRAC 0x20
#define MAX30102_DIETEMPCONFIG 0x21
// Part ID Registers
#define MAX30102_REVISIONID 0xFE
#define MAX30102_PARTID 0xFF
// Proximity Function Registers
#define MAX30102_PROXINTTHRESH 0x30
//Multi-LED Mode configuration (pg 22)
#define MAX30102_SLOT1_MASK 0xF8
#define MAX30102_SLOT2_MASK 0x8F
#define MAX30102_SLOT3_MASK 0xF8
#define MAX30102_SLOT4_MASK 0x8F
#define SLOT_NONE 0x00
#define SLOT_RED_LED 0x01
#define SLOT_IR_LED 0x02
#define SLOT_GREEN_LED 0x03
#define SLOT_NONE_PILOT 0x04
#define SLOT_RED_PILOT 0x05
#define SLOT_IR_PILOT 0x06
#define SLOT_GREEN_PILOT 0x07
//#define MAX30102_SAMPLEAVG_MASK (byte)~0b11100000
static const uint8_t MAX30102_SAMPLEAVG_MASK = (uint8_t)~0b11100000;
#define MAX30102_SAMPLEAVG_1 0x00
#define MAX30102_SAMPLEAVG_2 0x20
#define MAX30102_SAMPLEAVG_4 0x40
#define MAX30102_SAMPLEAVG_8 0x60
#define MAX30102_SAMPLEAVG_16 0x80
#define MAX30102_SAMPLEAVG_32 0xA0
#define MAX30102_ROLLOVER_MASK 0xEF
#define MAX30102_ROLLOVER_ENABLE 0x10
#define MAX30102_ROLLOVER_DISABLE 0x00
#define MAX30102_A_FULL_MASK 0xF0
#define MAX30102_EXPECTED_PARTID 0x15
#define MAX30102_SENSE_BUF_SIZE 32
#define I2C_BUFFER_LENGTH 32
//Configuration Options
//FIFO Configuration(Register address 0x08)
//sampleAverage(Table 3. Sample Averaging)
#define SAMPLEAVG_1 0
#define SAMPLEAVG_2 1
#define SAMPLEAVG_4 2
#define SAMPLEAVG_8 3
#define SAMPLEAVG_16 4
#define SAMPLEAVG_32 5
//Mode configuration(Register address 0x09)
//ledMode(Table 4. Mode Control)
#define MODE_REDONLY 2
#define MODE_RED_IR 3
#define MODE_MULTILED 7
//Particle sensing configuration(Register address 0x0A)
//adcRange(Table 5. SpO2 ADC Range Control)
#define ADCRANGE_2048 0
#define ADCRANGE_4096 1
#define ADCRANGE_8192 2
#define ADCRANGE_16384 3
//sampleRate(Table 6. SpO2 Sample Rate Control)
#define SAMPLERATE_50 0
#define SAMPLERATE_100 1
#define SAMPLERATE_200 2
#define SAMPLERATE_400 3
#define SAMPLERATE_800 4
#define SAMPLERATE_1000 5
#define SAMPLERATE_1600 6
#define SAMPLERATE_3200 7
//pulseWidth(Table 7. LED Pulse Width Control)
#define PULSEWIDTH_69 0
#define PULSEWIDTH_118 1
#define PULSEWIDTH_215 2
#define PULSEWIDTH_411 3
//Multi-LED Mode Control Registers(Register address 0x011)
//#define SLOT_NONE 0
//#define SLOT_RED_LED 1
//#define SLOT_IR_LED 2
typedef struct {
uint32_t red[MAX30102_SENSE_BUF_SIZE];
uint32_t IR[MAX30102_SENSE_BUF_SIZE];
uint8_t head;
uint8_t tail;
} sSenseBuf_t;
//senseBuf.red[senseBuf.head]
uint8_t activeLEDs = 2;
sSenseBuf_t senseBuf;
bool MAX30102_begin();
void MAX30102_enableAlmostFull(void);
void MAX30102_disableAlmostFull(void);
void MAX30102_enableDataReady(void);
void MAX30102_disableDataReady(void);
void MAX30102_enableALCOverflow(void);
void MAX30102_disableALCOverflow(void);
void MAX30102_enablePROXINT(void);
void MAX30102_disablePROXINT(void);
void MAX30102_enableDieTempReady(void);
void MAX30102_disableDieTempReady(void);
void MAX30102_enableFIFORollover(void);
void MAX30102_disableFIFORollover(void);
void MAX30102_setFIFOAlmostFull(uint8_t numberOfSamples);
void MAX30102_setFIFOAverage(uint8_t numberOfSamples);
void MAX30102_shutDown(void);
void MAX30102_wakeUp(void);
void MAX30102_setLEDMode(uint8_t mode);
void MAX30102_setADCRange(uint8_t adcRange);
void MAX30102_setSampleRate(uint8_t sampleRate);
void MAX30102_setPulseWidth(uint8_t pulseWidth);
void MAX30102_setPulseAmplitudeRed(uint8_t amplitude);
void MAX30102_setPulseAmplitudeIR(uint8_t amplitude);
void MAX30102_disableAllSlots(void);
uint8_t MAX30102_getWritePointer(void);
uint8_t MAX30102_getReadPointer(void);
uint8_t MAX30102_getPartID(void);
uint8_t MAX30102_getRevisionID(void);
void MAX30102_resetFIFO(void);
void MAX30102_softReset(void);
void MAX30102_enableSlot(uint8_t slotNumber, uint8_t device);
float MAX30102_readTemperatureC(void);
float MAX30102_readTemperatureF(void);
void MAX30102_sensorConfiguration(uint8_t ledBrightness, uint8_t sampleAverage, uint8_t ledMode, uint32_t sampleRate, uint32_t pulseWidth, uint32_t adcRange);
uint32_t MAX30102_getRed(void);
uint32_t MAX30102_getIR(void);
uint16_t MAX30102_getNewData(void);
void bitMask(uint8_t reg, uint8_t mask, uint8_t thing);
uint8_t MAX30102_available(void);
void MAX30102_nextSample(void);
void MAX30102_heartrateAndOxygenSaturation(int32_t* SPO2, int8_t* SPO2Valid, int32_t* heartRate, int8_t* heartRateValid);
bool MAX30102_begin()
{
if (MAX30102_getPartID() != MAX30102_EXPECTED_PARTID) {
return false;
}
return true;
}
void MAX30102_enableAlmostFull(void) {
bitMask(MAX30102_INTENABLE1, MAX30102_INT_A_FULL_MASK, MAX30102_INT_A_FULL_ENABLE);
}
void MAX30102_disableAlmostFull(void) {
bitMask(MAX30102_INTENABLE1, MAX30102_INT_A_FULL_MASK, MAX30102_INT_A_FULL_DISABLE);
}
void MAX30102_enableDataReady(void) {
bitMask(MAX30102_INTENABLE1, MAX30102_INT_DATA_RDY_MASK, MAX30102_INT_DATA_RDY_ENABLE);
}
void MAX30102_disableDataReady(void) {
bitMask(MAX30102_INTENABLE1, MAX30102_INT_DATA_RDY_MASK, MAX30102_INT_DATA_RDY_DISABLE);
}
void MAX30102_enableALCOverflow(void) {
bitMask(MAX30102_INTENABLE1, MAX30102_INT_ALC_OVF_MASK, MAX30102_INT_ALC_OVF_ENABLE);
}
void MAX30102_disableALCOverflow(void) {
bitMask(MAX30102_INTENABLE1, MAX30102_INT_ALC_OVF_MASK, MAX30102_INT_ALC_OVF_DISABLE);
}
void MAX30102_enablePROXINT(void) {
bitMask(MAX30102_INTENABLE1, MAX30102_INT_PROX_INT_MASK, MAX30102_INT_PROX_INT_ENABLE);
}
void MAX30102_disablePROXINT(void) {
bitMask(MAX30102_INTENABLE1, MAX30102_INT_PROX_INT_MASK, MAX30102_INT_PROX_INT_DISABLE);
}
void MAX30102_enableDieTempReady(void) {
bitMask(MAX30102_INTENABLE2, MAX30102_INT_DIE_TEMP_RDY_MASK, MAX30102_INT_DIE_TEMP_RDY_ENABLE);
}
void MAX30102_disableDieTempReady(void) {
bitMask(MAX30102_INTENABLE2, MAX30102_INT_DIE_TEMP_RDY_MASK, MAX30102_INT_DIE_TEMP_RDY_DISABLE);
}
void MAX30102_enableFIFORollover(void) {
bitMask(MAX30102_FIFOCONFIG, MAX30102_ROLLOVER_MASK, MAX30102_ROLLOVER_ENABLE);
}
void MAX30102_disableFIFORollover(void) {
bitMask(MAX30102_FIFOCONFIG, MAX30102_ROLLOVER_MASK, MAX30102_ROLLOVER_DISABLE);
}
void MAX30102_setFIFOAlmostFull(uint8_t numberOfSamples) {
bitMask(MAX30102_FIFOCONFIG, MAX30102_A_FULL_MASK, numberOfSamples);
}
void MAX30102_setFIFOAverage(uint8_t numberOfSamples) {
bitMask(MAX30102_FIFOCONFIG, MAX30102_SAMPLEAVG_MASK, numberOfSamples);
}
void MAX30102_shutDown(void) {
bitMask(MAX30102_MODECONFIG, MAX30102_SHUTDOWN_MASK, MAX30102_SHUTDOWN);
}
void MAX30102_wakeUp(void) {
bitMask(MAX30102_MODECONFIG, MAX30102_SHUTDOWN_MASK, MAX30102_WAKEUP);
}
void MAX30102_setLEDMode(uint8_t mode) {
bitMask(MAX30102_MODECONFIG, MAX30102_MODE_MASK, mode);
}
void MAX30102_setADCRange(uint8_t adcRange) {
bitMask(MAX30102_PARTICLECONFIG, MAX30102_ADCRANGE_MASK, adcRange);
}
void MAX30102_setSampleRate(uint8_t sampleRate) {
bitMask(MAX30102_PARTICLECONFIG, MAX30102_SAMPLERATE_MASK, sampleRate);
}
void MAX30102_setPulseWidth(uint8_t pulseWidth) {
bitMask(MAX30102_PARTICLECONFIG, MAX30102_PULSEWIDTH_MASK, pulseWidth);
}
void MAX30102_setPulseAmplitudeRed(uint8_t amplitude) {
i2c_write1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_LED1_PULSEAMP, amplitude);
}
void MAX30102_setPulseAmplitudeIR(uint8_t amplitude) {
i2c_write1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_LED2_PULSEAMP, amplitude);
}
void MAX30102_disableAllSlots(void) {
i2c_write1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_MULTILEDCONFIG1, 0);
i2c_write1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_MULTILEDCONFIG2, 0);
}
uint8_t MAX30102_getWritePointer(void) {
return (i2c_read1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_FIFOWRITEPTR));
}
uint8_t MAX30102_getReadPointer(void) {
return (i2c_read1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_FIFOREADPTR));
}
uint8_t MAX30102_getPartID(void) {
return i2c_read1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_PARTID);
}
uint8_t MAX30102_getRevisionID(void) {
return i2c_read1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_REVISIONID);
}
void MAX30102_resetFIFO(void) {
i2c_write1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_FIFOWRITEPTR, 0);
i2c_write1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_FIFOOVERFLOW, 0);
i2c_write1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_FIFOREADPTR, 0);
}
void MAX30102_softReset(void) {
bitMask(MAX30102_MODECONFIG, MAX30102_RESET_MASK, MAX30102_RESET);
uint8_t timeCount = 0;
while (timeCount <= 100)
{
uint8_t response = i2c_read1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_MODECONFIG);
if ((response & MAX30102_RESET) == 0) break; //We're done!
DELAY_milliseconds(1); //Let's not over burden the I2C bus
timeCount++;
}
}
void MAX30102_enableSlot(uint8_t slotNumber, uint8_t device) {
switch (slotNumber) {
case (1):
bitMask(MAX30102_MULTILEDCONFIG1, MAX30102_SLOT1_MASK, device);
break;
case (2):
bitMask(MAX30102_MULTILEDCONFIG1, MAX30102_SLOT2_MASK, device << 4);
break;
case (3):
bitMask(MAX30102_MULTILEDCONFIG2, MAX30102_SLOT3_MASK, device);
break;
case (4):
bitMask(MAX30102_MULTILEDCONFIG2, MAX30102_SLOT4_MASK, device << 4);
break;
default:
break;
}
}
float MAX30102_readTemperatureC(void) {
i2c_write1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_DIETEMPCONFIG, 0x01);
uint8_t count = 0;
while (count <= 100)
{
uint8_t response = i2c_read1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_INTSTAT2);
if ((response & MAX30102_INT_DIE_TEMP_RDY_ENABLE) > 0) break;
DELAY_milliseconds(1);
count++;
}
int8_t tempInt = i2c_read1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_DIETEMPINT);
uint8_t tempFrac = i2c_read1ByteRegister(MAX30102_IIC_ADDRESS, MAX30102_DIETEMPFRAC);
return (float)tempInt + ((float)tempFrac * 0.0625);
}
float MAX30102_readTemperatureF(void) {
float temp = MAX30102_readTemperatureC();
if (temp != -999.0) temp = temp * 1.8 + 32.0;
return (temp);
}
void MAX30102_sensorConfiguration(uint8_t ledBrightness, uint8_t sampleAverage, uint8_t ledMode, uint32_t sampleRate, uint32_t pulseWidth, uint32_t adcRange)
{
MAX30102_softReset();
//MAX30102_setFIFOAverage(sampleAverage);
if (sampleAverage == 1) MAX30102_setFIFOAverage(MAX30102_SAMPLEAVG_1); //No averaging per FIFO record
else if (sampleAverage == 2) MAX30102_setFIFOAverage(MAX30102_SAMPLEAVG_2);
else if (sampleAverage == 4) MAX30102_setFIFOAverage(MAX30102_SAMPLEAVG_4);
else if (sampleAverage == 8) MAX30102_setFIFOAverage(MAX30102_SAMPLEAVG_8);
else if (sampleAverage == 16) MAX30102_setFIFOAverage(MAX30102_SAMPLEAVG_16);
else if (sampleAverage == 32) MAX30102_setFIFOAverage(MAX30102_SAMPLEAVG_32);
else MAX30102_setFIFOAverage(MAX30102_SAMPLEAVG_4);
//MAX30102_setADCRange(adcRange);
if(adcRange < 4096) MAX30102_setADCRange(MAX30102_ADCRANGE_2048); //7.81pA per LSB
else if(adcRange < 8192) MAX30102_setADCRange(MAX30102_ADCRANGE_4096); //15.63pA per LSB
else if(adcRange < 16384) MAX30102_setADCRange(MAX30102_ADCRANGE_8192); //31.25pA per LSB
else if(adcRange == 16384) MAX30102_setADCRange(MAX30102_ADCRANGE_16384); //62.5pA per LSB
else MAX30102_setADCRange(MAX30102_ADCRANGE_2048);
//MAX30102_setSampleRate(sampleRate);
if (sampleRate < 100) MAX30102_setSampleRate(MAX30102_SAMPLERATE_50); //Take 50 samples per second
else if (sampleRate < 200) MAX30102_setSampleRate(MAX30102_SAMPLERATE_100);
else if (sampleRate < 400) MAX30102_setSampleRate(MAX30102_SAMPLERATE_200);
else if (sampleRate < 800) MAX30102_setSampleRate(MAX30102_SAMPLERATE_400);
else if (sampleRate < 1000) MAX30102_setSampleRate(MAX30102_SAMPLERATE_800);
else if (sampleRate < 1600) MAX30102_setSampleRate(MAX30102_SAMPLERATE_1000);
else if (sampleRate < 3200) MAX30102_setSampleRate(MAX30102_SAMPLERATE_1600);
else if (sampleRate == 3200) MAX30102_setSampleRate(MAX30102_SAMPLERATE_3200);
else MAX30102_setSampleRate(MAX30102_SAMPLERATE_50);
//MAX30102_setPulseWidth(pulseWidth);
if (pulseWidth < 118) MAX30102_setPulseWidth(MAX30102_PULSEWIDTH_69); //Page 26, Gets us 15 bit resolution
else if (pulseWidth < 215) MAX30102_setPulseWidth(MAX30102_PULSEWIDTH_118); //16 bit resolution
else if (pulseWidth < 411) MAX30102_setPulseWidth(MAX30102_PULSEWIDTH_215); //17 bit resolution
else if (pulseWidth == 411) MAX30102_setPulseWidth(MAX30102_PULSEWIDTH_411); //18 bit resolution
else MAX30102_setPulseWidth(MAX30102_PULSEWIDTH_69);
//Default is 0x1F which gets us 6.4mA
//ledBrightness = 0x02, 0.4mA - Presence detection of ~4 inch
//ledBrightness = 0x1F, 6.4mA - Presence detection of ~8 inch
//ledBrightness = 0x7F, 25.4mA - Presence detection of ~8 inch
//ledBrightness = 0xFF, 50.0mA - Presence detection of ~12 inch
MAX30102_setPulseAmplitudeRed(ledBrightness);
MAX30102_setPulseAmplitudeIR(ledBrightness);
MAX30102_enableSlot(1, SLOT_RED_LED);
if (ledMode > 1) MAX30102_enableSlot(2, SLOT_IR_LED);
//MAX30102_setLEDMode(ledMode);
if (ledMode == 3) MAX30102_setLEDMode(MAX30102_MODE_MULTILED); //Watch all three LED channels
else if (ledMode == 2) MAX30102_setLEDMode(MAX30102_MODE_REDIRONLY); //Red and IR
else MAX30102_setLEDMode(MAX30102_MODE_REDONLY); //Red only
activeLEDs = ledMode;
MAX30102_enableFIFORollover();
MAX30102_resetFIFO();
}
uint32_t MAX30102_getRed(void)
{
MAX30102_getNewData();
return (senseBuf.red[senseBuf.head]);
}
uint32_t MAX30102_getIR(void)
{
MAX30102_getNewData();
return (senseBuf.IR[senseBuf.head]);
}
uint16_t MAX30102_getNewData(void)
{
uint8_t readPointer = MAX30102_getReadPointer();
uint8_t writePointer = MAX30102_getWritePointer();
int numberOfSamples = 0;
if (readPointer != writePointer)
{
numberOfSamples = writePointer - readPointer;
if (numberOfSamples < 0) numberOfSamples += 32;
int bytesNeedToRead = numberOfSamples * activeLEDs * 3;
while (bytesNeedToRead > 0)
{
int toGet = bytesNeedToRead;
if (toGet > I2C_BUFFER_LENGTH)
{
toGet = I2C_BUFFER_LENGTH - (I2C_BUFFER_LENGTH % (activeLEDs * 3));
}
bytesNeedToRead -= toGet;
while (toGet > 0)
{
senseBuf.head++;
senseBuf.head %= MAX30102_SENSE_BUF_SIZE;
uint32_t tempBuf = 0;
if (activeLEDs > 1)
{
uint8_t temp[6];
uint8_t tempex;
i2c_readDataBlock(MAX30102_IIC_ADDRESS, MAX30102_FIFODATA, temp, 6);
for(uint8_t i = 0; i < 3; i++){
tempex = temp[i];
temp[i] = temp[5-i];
temp[5-i] = tempex;
}
memcpy(&tempBuf, temp, 3*sizeof(temp[0]));
tempBuf &= 0x3FFFF;
senseBuf.IR[senseBuf.head] = tempBuf;
//debug_printIoTAppMsg("ir = %d",senseBuf.IR[senseBuf.head]);
memcpy(&tempBuf, temp+3, 3*sizeof(temp[0]));
tempBuf &= 0x3FFFF;
senseBuf.red[senseBuf.head] = tempBuf;
//debug_printIoTAppMsg("red = %d",senseBuf.red[senseBuf.head]);
}
else
{
uint8_t temp[3];
uint8_t tempex;
i2c_readDataBlock(MAX30102_IIC_ADDRESS, MAX30102_FIFODATA, temp, 3);
tempex = temp[0];
temp[0] = temp[2];
temp[2] = tempex;
memcpy(&tempBuf, temp, 3*sizeof(temp[0]));
tempBuf &= 0x3FFFF;
senseBuf.red[senseBuf.head] = tempBuf;
}
toGet -= activeLEDs * 3;
}
}
}
return (numberOfSamples);
}
uint8_t MAX30102_available(void)
{
int8_t numberOfSamples = senseBuf.head - senseBuf.tail;
if (numberOfSamples < 0) numberOfSamples += MAX30102_SENSE_BUF_SIZE;
return (numberOfSamples);
}
void MAX30102_nextSample(void)
{
if(MAX30102_available()) //Only advance the tail if new data is available
{
senseBuf.tail++;
senseBuf.tail %= MAX30102_SENSE_BUF_SIZE; //Wrap condition
}
}
/*
void MAX30102_heartrateAndOxygenSaturation(int32_t* SPO2, int8_t* SPO2Valid, int32_t* heartRate, int8_t* heartRateValid)
{
uint32_t irBuffer[100];
uint32_t redBuffer[100];
int32_t bufferLength = 100;
for (uint8_t i = 0 ; i < bufferLength ; ) {
int sampleNumber = MAX30102_getNewData();
int8_t numberOfSamples = senseBuf.head - senseBuf.tail;
if (numberOfSamples < 0) {
numberOfSamples += MAX30102_SENSE_BUF_SIZE;
}
while(numberOfSamples--) {
redBuffer[i] = senseBuf.red[senseBuf.tail];
irBuffer[i] = senseBuf.IR[senseBuf.tail];
senseBuf.tail++;
senseBuf.tail %= MAX30102_SENSE_BUF_SIZE;
i++;
if(i == bufferLength) break;
}
}
maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, SPO2, SPO2Valid, heartRate, heartRateValid);
}
*/
void bitMask(uint8_t reg, uint8_t mask, uint8_t thing)
{
uint8_t originalContents = i2c_read1ByteRegister(MAX30102_IIC_ADDRESS, reg);
originalContents = originalContents & mask;
i2c_write1ByteRegister(MAX30102_IIC_ADDRESS, reg, originalContents | thing);
}
#ifndef MLX90614_H_
#define MLX90614_H_
// MLX90614 Default I2C Address //
#define MLX90614_DEFAULT_ADDRESS 0x5A
// MLX90614 RAM and EEPROM Addresses //
#define MLX90614_REGISTER_TA 0x06
#define MLX90614_REGISTER_TOBJ1 0x07
#define MLX90614_REGISTER_TOBJ2 0x08
#define MLX90614_REGISTER_TOMAX 0x20
#define MLX90614_REGISTER_TOMIN 0x21
#define MLX90614_REGISTER_PWMCTRL 0x22
#define MLX90614_REGISTER_TARANGE 0x23
#define MLX90614_REGISTER_KE 0x24
#define MLX90614_REGISTER_CONFIG 0x25
#define MLX90614_REGISTER_ADDRESS 0x2E
#define MLX90614_REGISTER_ID0 0x3C
#define MLX90614_REGISTER_ID1 0x3D
#define MLX90614_REGISTER_ID2 0x3E
#define MLX90614_REGISTER_ID3 0x3F
#define MLX90614_REGISTER_SLEEP 0xFF
uint16_t MLX90614_ReadAmbTemp(void);
uint16_t MLX90614_ReadObj_1_Temp(void);
uint16_t MLX90614_ReadObj_2_Temp(void);
uint16_t MLX90614_ReadObj_2_Temp(void);
uint16_t MLX90614_ReadMaxTemp(void);
uint16_t MLX90614_ReadMinTemp(void);
uint8_t MLX90614_ReadAddress(void);
float MLX90614_ambTempC(void) ;
float MLX90614_obj1TempC(void) ;
float MLX90614_obj2TempC(void) ;
float MLX90614_ambTempF(void) ;
float MLX90614_obj1TempF(void) ;
float MLX90614_obj2TempF(void) ;
#endif /* MLX90614_H_ */
/*
* File: MLX90614.c
* Author: Md. Khairul Alam
*
* Created on December 30, 2020, 11:13 PM
*/
#include "MLX90614.h"
#include "drivers/i2c_simple_master.h"
#include "debug_print.h"
uint16_t MLX90614_ReadAmbTemp(void)
{
uint8_t temp[3];
uint16_t rawTemperature;
i2c_readDataBlock(MLX90614_DEFAULT_ADDRESS, MLX90614_REGISTER_TA, temp, 3);
rawTemperature = temp[1] << 8 | temp[2];
return rawTemperature;
//debug_printIoTAppMsg("aa= %d ba = %d ca = %d",temp[0], temp[1], temp[2]);
}
uint16_t MLX90614_ReadObj_1_Temp(void)
{
uint8_t temp[3];
uint16_t rawTemperature;
i2c_readDataBlock(MLX90614_DEFAULT_ADDRESS, MLX90614_REGISTER_TOBJ1, temp, 3);
rawTemperature = temp[1] << 8 | temp[2];
return rawTemperature;
//debug_printIoTAppMsg("ao = %d bo = %d co = %d",temp[0], temp[1], temp[2]);
}
uint16_t MLX90614_ReadObj_2_Temp(void)
{
uint8_t temp[3];
uint16_t rawTemperature;
i2c_readDataBlock(MLX90614_DEFAULT_ADDRESS, MLX90614_REGISTER_TOBJ2, temp, 3);
rawTemperature = temp[1] << 8 | temp[2];
return rawTemperature;
//debug_printIoTAppMsg("ao = %d bo = %d co = %d",temp[0], temp[1], temp[2]);
}
uint16_t MLX90614_ReadMaxTemp(void)
{
uint8_t temp[3];
uint16_t rawTemperature;
i2c_readDataBlock(MLX90614_DEFAULT_ADDRESS, MLX90614_REGISTER_TOMAX, temp, 3);
rawTemperature = temp[1] << 8 | temp[2];
return rawTemperature;
//debug_printIoTAppMsg("ao = %d bo = %d co = %d",temp[0], temp[1], temp[2]);
}
uint16_t MLX90614_ReadMinTemp(void)
{
uint8_t temp[3];
uint16_t rawTemperature;
i2c_readDataBlock(MLX90614_DEFAULT_ADDRESS, MLX90614_REGISTER_TOMIN, temp, 3);
rawTemperature = temp[1] << 8 | temp[2];
return rawTemperature;
//debug_printIoTAppMsg("ao = %d bo = %d co = %d",temp[0], temp[1], temp[2]);
}
uint8_t MLX90614_ReadAddress(void)
{
uint8_t temp[3];
i2c_readDataBlock(MLX90614_DEFAULT_ADDRESS, MLX90614_REGISTER_ADDRESS, temp, 3);
return temp[2];
//debug_printIoTAppMsg("addo = %d bddo = %d cddo = %d",temp[0], temp[1], temp[2]);
}
float MLX90614_ambTempC(void)
{
float temp = MLX90614_ReadAmbTemp();
temp *= .02;
temp -= 273.15;
return temp;
}
float MLX90614_obj1TempC(void)
{
float temp = MLX90614_ReadObj_1_Temp();
temp *= .02;
temp -= 273.15;
return temp;
}
float MLX90614_obj2TempC(void)
{
float temp = MLX90614_ReadObj_2_Temp();
temp *= .02;
temp -= 273.15;
return temp;
}
float MLX90614_ambTempF(void)
{
return (MLX90614_ambTempC() * 9/5) + 32;
}
float MLX90614_obj1TempF(void)
{
return (MLX90614_obj1TempC() * 9/5) + 32;
}
float MLX90614_obj2TempF(void)
{
return (MLX90614_obj2TempC() * 9/5) + 32;
}
#ifndef SSD1306_H_
#define SSD1306_H_
#define DAT 0x60
#define CMD 0x00
#define Set_Lower_Column_Start_Address_CMD 0x00
#define Set_Higher_Column_Start_Address_CMD 0x10
#define Set_Memory_Addressing_Mode_CMD 0x20
#define Set_Column_Address_CMD 0x21
#define Set_Page_Address_CMD 0x22
#define Set_Display_Start_Line_CMD 0x40
#define Set_Contrast_Control_CMD 0x81
#define Set_Charge_Pump_CMD 0x8D
#define Set_Segment_Remap_CMD 0xA0
#define Set_Entire_Display_ON_CMD 0xA4
#define Set_Normal_or_Inverse_Display_CMD 0xA6
#define Set_Multiplex_Ratio_CMD 0xA8
#define Set_Display_ON_or_OFF_CMD 0xAE
#define Set_Page_Start_Address_CMD 0xB0
#define Set_COM_Output_Scan_Direction_CMD 0xC0
#define Set_Display_Offset_CMD 0xD3
#define Set_Display_Clock_CMD 0xD5
#define Set_Pre_charge_Period_CMD 0xD9
#define Set_Common_HW_Config_CMD 0xDA
#define Set_VCOMH_Level_CMD 0xDB
#define Set_NOP_CMD 0xE3
#define Horizontal_Addressing_Mode 0x00
#define Vertical_Addressing_Mode 0x01
#define Page_Addressing_Mode 0x02
#define Disable_Charge_Pump 0x00
#define Enable_Charge_Pump 0x04
#define Column_Address_0_Mapped_to_SEG0 0x00
#define Column_Address_0_Mapped_to_SEG127 0x01
#define Normal_Display 0x00
#define Entire_Display_ON 0x01
#define Non_Inverted_Display 0x00
#define Inverted_Display 0x01
#define Display_OFF 0x00
#define Display_ON 0x01
#define Scan_from_COM0_to_63 0x00
#define Scan_from_COM63_to_0 0x08
#define x_size 128
#define x_max x_size
#define x_min 0
#define y_size 64
#define y_max 8
#define y_min 0
#define YES 1
#define NO 0
#define ROUND 1
#define SQUARE 0
#define buffer_size 1024//(x_max * y_max)
// Define command macros
#define OLED_SETCONTRAST 0x81
#define OLED_DISPLAYALLON_RESUME 0xA4
#define OLED_DISPLAYALLON 0xA5
#define OLED_NORMALDISPLAY 0xA6
#define OLED_INVERTDISPLAY 0xA7
#define OLED_DISPLAYOFF 0xAE
#define OLED_DISPLAYON 0xAF
#define OLED_SETDISPLAYOFFSET 0xD3
#define OLED_SETCOMPINS 0xDA
#define OLED_SETVCOMDETECT 0xDB
#define OLED_SETDISPLAYCLOCKDIV 0xD5
#define OLED_SETPRECHARGE 0xD9
#define OLED_SETMULTIPLEX 0xA8
#define OLED_SETLOWCOLUMN 0x00
#define OLED_SETHIGHCOLUMN 0x10
#define OLED_SETSTARTLINE 0x40
#define OLED_MEMORYMODE 0x20
#define OLED_COLUMNADDR 0x21
#define OLED_PAGEADDR 0x22
#define OLED_COMSCANINC 0xC0
#define OLED_COMSCANDEC 0xC8
#define OLED_SEGREMAP 0xA0
#define OLED_CHARGEPUMP 0x8D
#define OLED_ADDRESS 0x3C
const uint8_t OledFont[][8] =
{
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x5F,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x07,0x00,0x07,0x00,0x00,0x00},
{0x00,0x14,0x7F,0x14,0x7F,0x14,0x00,0x00},
{0x00,0x24,0x2A,0x7F,0x2A,0x12,0x00,0x00},
{0x00,0x23,0x13,0x08,0x64,0x62,0x00,0x00},
{0x00,0x36,0x49,0x55,0x22,0x50,0x00,0x00},
{0x00,0x00,0x05,0x03,0x00,0x00,0x00,0x00},
{0x00,0x1C,0x22,0x41,0x00,0x00,0x00,0x00},
{0x00,0x41,0x22,0x1C,0x00,0x00,0x00,0x00},
{0x00,0x08,0x2A,0x1C,0x2A,0x08,0x00,0x00},
{0x00,0x08,0x08,0x3E,0x08,0x08,0x00,0x00},
{0x00,0xA0,0x60,0x00,0x00,0x00,0x00,0x00},
{0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x00},
{0x00,0x60,0x60,0x00,0x00,0x00,0x00,0x00},
{0x00,0x20,0x10,0x08,0x04,0x02,0x00,0x00},
{0x00,0x3E,0x51,0x49,0x45,0x3E,0x00,0x00},
{0x00,0x00,0x42,0x7F,0x40,0x00,0x00,0x00},
{0x00,0x62,0x51,0x49,0x49,0x46,0x00,0x00},
{0x00,0x22,0x41,0x49,0x49,0x36,0x00,0x00},
{0x00,0x18,0x14,0x12,0x7F,0x10,0x00,0x00},
{0x00,0x27,0x45,0x45,0x45,0x39,0x00,0x00},
{0x00,0x3C,0x4A,0x49,0x49,0x30,0x00,0x00},
{0x00,0x01,0x71,0x09,0x05,0x03,0x00,0x00},
{0x00,0x36,0x49,0x49,0x49,0x36,0x00,0x00},
{0x00,0x06,0x49,0x49,0x29,0x1E,0x00,0x00},
{0x00,0x00,0x36,0x36,0x00,0x00,0x00,0x00},
{0x00,0x00,0xAC,0x6C,0x00,0x00,0x00,0x00},
{0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x00},
{0x00,0x14,0x14,0x14,0x14,0x14,0x00,0x00},
{0x00,0x41,0x22,0x14,0x08,0x00,0x00,0x00},
{0x00,0x02,0x01,0x51,0x09,0x06,0x00,0x00},
{0x00,0x32,0x49,0x79,0x41,0x3E,0x00,0x00},
{0x00,0x7E,0x09,0x09,0x09,0x7E,0x00,0x00},
{0x00,0x7F,0x49,0x49,0x49,0x36,0x00,0x00},
{0x00,0x3E,0x41,0x41,0x41,0x22,0x00,0x00},
{0x00,0x7F,0x41,0x41,0x22,0x1C,0x00,0x00},
{0x00,0x7F,0x49,0x49,0x49,0x41,0x00,0x00},
{0x00,0x7F,0x09,0x09,0x09,0x01,0x00,0x00},
{0x00,0x3E,0x41,0x41,0x51,0x72,0x00,0x00},
{0x00,0x7F,0x08,0x08,0x08,0x7F,0x00,0x00},
{0x00,0x41,0x7F,0x41,0x00,0x00,0x00,0x00},
{0x00,0x20,0x40,0x41,0x3F,0x01,0x00,0x00},
{0x00,0x7F,0x08,0x14,0x22,0x41,0x00,0x00},
{0x00,0x7F,0x40,0x40,0x40,0x40,0x00,0x00},
{0x00,0x7F,0x02,0x0C,0x02,0x7F,0x00,0x00},
{0x00,0x7F,0x04,0x08,0x10,0x7F,0x00,0x00},
{0x00,0x3E,0x41,0x41,0x41,0x3E,0x00,0x00},
{0x00,0x7F,0x09,0x09,0x09,0x06,0x00,0x00},
{0x00,0x3E,0x41,0x51,0x21,0x5E,0x00,0x00},
{0x00,0x7F,0x09,0x19,0x29,0x46,0x00,0x00},
{0x00,0x26,0x49,0x49,0x49,0x32,0x00,0x00},
{0x00,0x01,0x01,0x7F,0x01,0x01,0x00,0x00},
{0x00,0x3F,0x40,0x40,0x40,0x3F,0x00,0x00},
{0x00,0x1F,0x20,0x40,0x20,0x1F,0x00,0x00},
{0x00,0x3F,0x40,0x38,0x40,0x3F,0x00,0x00},
{0x00,0x63,0x14,0x08,0x14,0x63,0x00,0x00},
{0x00,0x03,0x04,0x78,0x04,0x03,0x00,0x00},
{0x00,0x61,0x51,0x49,0x45,0x43,0x00,0x00},
{0x00,0x7F,0x41,0x41,0x00,0x00,0x00,0x00},
{0x00,0x02,0x04,0x08,0x10,0x20,0x00,0x00},
{0x00,0x41,0x41,0x7F,0x00,0x00,0x00,0x00},
{0x00,0x04,0x02,0x01,0x02,0x04,0x00,0x00},
{0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00},
{0x00,0x01,0x02,0x04,0x00,0x00,0x00,0x00},
{0x00,0x20,0x54,0x54,0x54,0x78,0x00,0x00},
{0x00,0x7F,0x48,0x44,0x44,0x38,0x00,0x00},
{0x00,0x38,0x44,0x44,0x28,0x00,0x00,0x00},
{0x00,0x38,0x44,0x44,0x48,0x7F,0x00,0x00},
{0x00,0x38,0x54,0x54,0x54,0x18,0x00,0x00},
{0x00,0x08,0x7E,0x09,0x02,0x00,0x00,0x00},
{0x00,0x18,0xA4,0xA4,0xA4,0x7C,0x00,0x00},
{0x00,0x7F,0x08,0x04,0x04,0x78,0x00,0x00},
{0x00,0x00,0x7D,0x00,0x00,0x00,0x00,0x00},
{0x00,0x80,0x84,0x7D,0x00,0x00,0x00,0x00},
{0x00,0x7F,0x10,0x28,0x44,0x00,0x00,0x00},
{0x00,0x41,0x7F,0x40,0x00,0x00,0x00,0x00},
{0x00,0x7C,0x04,0x18,0x04,0x78,0x00,0x00},
{0x00,0x7C,0x08,0x04,0x7C,0x00,0x00,0x00},
{0x00,0x38,0x44,0x44,0x38,0x00,0x00,0x00},
{0x00,0xFC,0x24,0x24,0x18,0x00,0x00,0x00},
{0x00,0x18,0x24,0x24,0xFC,0x00,0x00,0x00},
{0x00,0x00,0x7C,0x08,0x04,0x00,0x00,0x00},
{0x00,0x48,0x54,0x54,0x24,0x00,0x00,0x00},
{0x00,0x04,0x7F,0x44,0x00,0x00,0x00,0x00},
{0x00,0x3C,0x40,0x40,0x7C,0x00,0x00,0x00},
{0x00,0x1C,0x20,0x40,0x20,0x1C,0x00,0x00},
{0x00,0x3C,0x40,0x30,0x40,0x3C,0x00,0x00},
{0x00,0x44,0x28,0x10,0x28,0x44,0x00,0x00},
{0x00,0x1C,0xA0,0xA0,0x7C,0x00,0x00,0x00},
{0x00,0x44,0x64,0x54,0x4C,0x44,0x00,0x00},
{0x00,0x08,0x36,0x41,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00},
{0x00,0x41,0x36,0x08,0x00,0x00,0x00,0x00},
{0x00,0x02,0x01,0x01,0x02,0x01,0x00,0x00},
{0x00,0x02,0x05,0x05,0x02,0x00,0x00,0x00},
};
/*
const uint8_t oledFont8x16 [][16] =
{
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0
{0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00}, // ! 1
{0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // "2
{0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00}, // # 3
{0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00}, // $ 4
{0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1c,0x03,0x1E,0x21,0x1E,0x00}, // % 5
{0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10}, // & 6
{0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // '7
{0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00}, // (8
{0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00}, // ) 9
{0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00}, // * 10
{0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00}, // + 11
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00}, // , 12
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01}, // - 13
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00}, // . 14
{0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00}, // / 15
{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00}, // 0 16
{0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00}, // 1 17
{0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00}, // 2 18
{0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00}, // 3 19
{0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00}, // 4 20
{0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00}, // 5 21
{0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00}, // 6 22
{0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00}, // 7 23
{0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1c,0x22,0x21,0x21,0x22,0x1c,0x00}, // 8 24
{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00}, // 9 25
{0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00}, // : 26
{0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00}, // ; 27
{0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00}, // <28
{0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00}, // = 29
{0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00}, // > 30
{0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00}, // ? 31
{0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00}, // @ 32
{0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20}, // A 33
{0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00}, // B 34
{0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00}, // C 35
{0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00}, // D 36
{0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00}, // E 37
{0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00}, // F 38
{0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00}, // G 39
{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20}, // H 40
{0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00}, // I 41
{0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00}, // J 42
{0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00}, // K 43
{0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00}, // L 44
{0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00}, // M 45
{0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00}, // N 46
{0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00}, // O 47
{0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00}, // P 48
{0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00}, // Q 49
{0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20}, // R 50
{0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1c,0x00}, // S 51
{0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00}, // T 52
{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00}, // U 53
{0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00}, // V 54
{0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00}, // W 55
{0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20}, // X 56
{0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00}, // Y 57
{0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00}, // Z 58
{0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00}, // [59
{0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00}, // \ 60
{0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00}, // ] 61
{0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // ^ 62
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, // _ 63
{0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // `64
{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20}, // a 65
{0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00}, // b 66
{0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00}, // c 67
{0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20}, // d 68
{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00}, // and 69
{0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00}, // f 70
{0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00}, // g 71
{0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20}, // h 72
{0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00}, // i 73
{0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00}, // j 74
{0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00}, // k 75
{0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00}, // l 76
{0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F}, // m 77
{0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20}, // n 78
{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00}, // o 79
{0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00}, // p 80
{0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80}, // q 81
{0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00}, // r 82
{0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00}, // s 83
{0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00}, // t 84
{0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20}, // u 85
{0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00}, // v 86
{0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00}, // w 87
{0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00}, // x 88
{0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00}, // y 89
{0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00}, // z 90
{0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40}, // {91
{0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00}, // | 92
{0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00}, // } 93
{0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // ~ 94
};
*/
unsigned char buffer[1024];
void OLED_Command(uint8_t temp);
void OLED_Data(uint8_t temp);
void OLED_init2();
void OLED_YX(unsigned char Row, unsigned char Column); // *warning!* max 4 rows
void OLED_PutChar( char ch );
void OLED_Clear();
void OLED_Write_String( char *s );
void OLED_init();
void OLED_write(unsigned char value, unsigned char control_byte);
void OLED_gotoxy(unsigned char x_pos, unsigned char y_pos);
void OLED_fill(unsigned char bmp_data);
void OLED_print_Image(const unsigned char *bmp, unsigned char pixel);
void OLED_clear_screen();
void OLED_clear_buffer();
void OLED_cursor(unsigned char x_pos, unsigned char y_pos);
void OLED_draw_bitmap(unsigned char xb, unsigned char yb, unsigned char xe, unsigned char ye, unsigned char *bmp_img);
void OLED_print_char(unsigned char x_pos, unsigned char y_pos, unsigned char ch);
void OLED_print_string(unsigned char x_pos, unsigned char y_pos, unsigned char *ch);
void OLED_print_chr(unsigned char x_pos, unsigned char y_pos, signed int value);
void OLED_print_int(unsigned char x_pos, unsigned char y_pos, signed long value);
void OLED_print_decimal(unsigned char x_pos, unsigned char y_pos, unsigned int value, unsigned char points);
void OLED_print_float(unsigned char x_pos, unsigned char y_pos, float value, unsigned char points);
void Draw_Pixel(unsigned char x_pos, unsigned char y_pos, unsigned char colour);
void Draw_Line(signed int x1, signed int y1, signed int x2, signed int y2, unsigned char colour);
void Draw_Rectangle(signed int x1, signed int y1, signed int x2, signed int y2, unsigned char fill, unsigned char colour, unsigned char type);
void Draw_Circle(signed int xc, signed int yc, signed int radius, unsigned char fill, unsigned char colour);
#endif /* SSD1306_H_ */
Comments