Md. Khairul Alam
Published © MIT

Remote Patient Monitoring System

A health monitoring system for rural people of Bangladesh

AdvancedWork in progressOver 1 day890
Remote Patient Monitoring System

Things used in this project

Hardware components

AVR-IoT WA Development Board
Microchip AVR-IoT WA Development Board
×1
DFRobot MAX30102 Heart Rate and Oximeter Sensor Breakout Board
×1
Gravity I2C OLED-2864 Display
DFRobot Gravity I2C OLED-2864 Display
×1
Seeed Studio Grove - Single-Point Infrared Thermometer - MLX90614
×1
Li-Ion Battery 1000mAh
Li-Ion Battery 1000mAh
×1
HC-05 Bluetooth Module
HC-05 Bluetooth Module
×1

Software apps and online services

MPLAB X IDE
Microchip MPLAB X IDE
MPLAB Code Configurator
Microchip MPLAB Code Configurator
AWS IoT
Amazon Web Services AWS IoT
AWS SNS
Amazon Web Services AWS SNS

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Custom parts and enclosures

Main Box Top

Main Box Bottom

Thermometer Handle

Thermometer Base

Schematics

Connection Diagram

Code

SSD1306.c

C/C++
#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);
}

algorithm

C/C++
/*
 * 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;
  }
}

MAX30102.h

C/C++
pulse oximiter
#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_ */

MAX30102.c

C/C++
/*
 * 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);
}

MLX90614.h

C/C++
ir thermometer
#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_ */

MLX90614.c

C/C++
/*
 * 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;
}

SSD1306.h

C/C++
OLED display
#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_ */

GitHub Link

Credits

Md. Khairul Alam

Md. Khairul Alam

68 projects • 584 followers
Developer, Maker & Hardware Hacker. Currently working as a faculty at the University of Asia Pacific, Dhaka, Bangladesh.

Comments