Measure FIFO/RED/IR/Green/Blue data from MAX86916 using NRF52840

Hi All, I'm working with MAX86916 sensor and trying to measure the FIFO/RED/IR/GREEN/BLUE data to measure SPO2, Heart Rate, Heart Beat but I'm not able to read any data on serial monitor. I followed MAx30101 and many other libraries to develop the library for this sensor but still failed to get any data. this is the data sheet.

 MAX86916-Datasheet

  • Here is the H 

    /****************************************************************************
    ** Copyright (C) 2020 MikroElektronika d.o.o.
    ** Contact: https://www.mikroe.com/contact
    **
    ** 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 THE AUTHORS OR COPYRIGHT HOLDERS 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.
    ****************************************************************************/
    
    /*!
     * @file heartrate10.h
     * @brief This file contains API for Heart Rate 10 Click Driver.
     */
    
    #ifndef MAX86916_H
    #define MAX86916_H
    #include "nrf_delay.h"
    
    //I2C Pins Settings, you change them to any other pins
    #define TWI_SCL_M           8         //I2C SCL Pin
    #define TWI_SDA_M           7         //I2C SDA Pin
    
    
    #define MAX86916_ADDRESS_LEN  1         //MPU6050
    #define MAX86916_ADDRESS     (0xAE>>1)  //MPU6050 Device Address
    #define MAX86916_WHO_AM_I     0x57U     //MPU6050 ID
    
    
    #define MAX86916_OUT                     0x2B
    
    
    #define ADDRESS_WHO_AM_I          (0x64U) //  WHO_AM_I register identifies the device. Expected value is 0x68.
    #define ADDRESS_SIGNAL_PATH_RESET (0x57U) // 
    
    /**
     * Any initialization code needed for MCU to function properly.
     * Do not remove this line or clock might not be set correctly.
     */
    
    
    //#include "drv_digital_out.h"
    //#include "drv_digital_in.h"
    //#include "drv_i2c_master.h"
    
    /*!
     * @addtogroup heartrate10 Heart Rate 10 Click Driver
     * @brief API for configuring and manipulating Heart Rate 10 Click driver.
     * @{
     */
    
    /**
     * @defgroup heartrate10_reg Heart Rate 10 Registers List
     * @brief List of registers of Heart Rate 10 Click driver.
     */
    
    /**
     * @addtogroup heartrate10_reg
     * @{
     */
    
    /**
     * @brief Heart Rate 10 communication register.
     * @details Specified registers for communication with Heart Rate 10 Click.
     */
    #define MAX86916_REG_INT_STATUS              0x00
    #define MAX86916_REG_INT_ENABLE              0x02
    #define MAX86916_REG_FIFO_WR_PTR             0x04
    #define MAX86916_REG_FIFO_OVF_CNT            0x05
    #define MAX86916_REG_FIFO_RD_PTR             0x06
    #define MAX86916_REG_FIFO_DATA               0x07
    #define MAX86916_REG_FIFO_CFG                0x08
    #define MAX86916_REG_MODE_CFG1               0x09
    #define MAX86916_REG_MODE_CFG2               0x0A
    #define MAX86916_REG_LED1_PA                 0x0C
    #define MAX86916_REG_LED2_PA                 0x0D
    #define MAX86916_REG_LED3_PA                 0x0E
    #define MAX86916_REG_LED4_PA                 0x0F
    #define MAX86916_REG_LED_RANGE               0x11
    #define MAX86916_REG_PILOT_PA                0x12
    #define MAX86916_REG_LED_SEQ1                0x13
    #define MAX86916_REG_LED_SEQ2                0x14
    #define MAX86916_REG_DAC1_CROSSTALK_CODE     0x26
    #define MAX86916_REG_DAC2_CROSSTALK_CODE     0x27
    #define MAX86916_REG_DAC3_CROSSTALK_CODE     0x28
    #define MAX86916_REG_DAC4_CROSSTALK_CODE     0x29
    #define MAX86916_REG_PROX_INT_THRESHOLD      0x30
    #define MAX86916_REG_LED_COMPARATOR_EN       0x31
    #define MAX86916_REG_LED_COMPARATOR_STATUS   0x32
    #define MAX86916_REG_REV_ID                  0xFE
    #define MAX86916_REG_PART_ID                 0xFF
    
    
    
    
    void twi_master_init(void); // initialize the twi communication
    uint32_t max86916_init(void);    // initialize the mpu6050
    /*! @} */ // heartrate10_reg
    
    /**
     * @defgroup heartrate10_set Heart Rate 10 Registers Settings
     * @brief Settings for registers of Heart Rate 10 Click driver.
     */
    
    /**
     * @addtogroup heartrate10_set
     * @{
     */
    
    /**
     * @brief Heart Rate 10 description setting.
     * @details Specified setting for description of Heart Rate 10 Click driver.
     */
    //#define MAX86916_OUT                     0x2B
    
    /**
     * @brief Heart Rate 10 device address setting.
     * @details Specified setting for device slave address selection of
     * Heart Rate 10 Click driver.
     */
    //#define MAX86916_SET_DEV_ADDR                0x57
    
    /*! @} */ // heartrate10_set
    
    /**
     * @defgroup heartrate10_map Heart Rate 10 MikroBUS Map
     * @brief MikroBUS pin mapping of Heart Rate 10 Click driver.
     */
    
    /**
     * @addtogroup heartrate10_map
     * @{
     */
    
    /**
     * @brief MikroBUS pin mapping.
     * @details Mapping pins of Heart Rate 10 Click to the selected MikroBUS.
     */
    /*#define HEARTRATE10_MAP_MIKROBUS( cfg, mikrobus ) \
        cfg.scl = MIKROBUS( mikrobus, TWI_SCL_M ); \
        cfg.sda = MIKROBUS( mikrobus, TWI_SCL_M ); \
        cfg.int_pin = MIKROBUS( mikrobus, MIKROBUS_INT )
    
    /*! @} */ // heartrate10_map
    /*! @} */ // heartrate10
    
    /**
     * @brief Heart Rate 10 Click context object.
     * @details Context object definition of Heart Rate 10 Click driver.
     */
    /*typedef struct
    {
        // Input pins
        digital_in_t  int_pin;  /**< Interrupt pin. */
    
        // Modules
    /*    i2c_master_t i2c;       /**< I2C driver object. */
    
        // I2C slave address
     /*   uint8_t slave_address;  /**< Device slave address (used for I2C driver). */
    
    /*} heartrate10_t;
    
    /**
     * @brief Heart Rate 10 Click configuration object.
     * @details Configuration object definition of Heart Rate 10 Click driver.
     */
    /*typedef struct
    {
        pin_name_t  TWI_SCL_M;        /**< Clock pin descriptor for I2C driver. */
      /*  pin_name_t  TWI_SCL_M;        /**< Bidirectional data pin descriptor for I2C driver. */
        
      /*  pin_name_t  int_pin;    /**< Interrupt pin. */
    
    /*    uint32_t  i2c_speed;    /**< I2C serial speed. */
    /*    uint8_t   i2c_address;  /**< I2C slave address. */
    
    /*} heartrate10_cfg_t;
    
    /**
     * @brief Heart Rate 10 Click return value data.
     * @details Predefined enum values for driver return values.
     */
    
    
    /*!
     * @addtogroup heartrate10 Heart Rate 10 Click Driver
     * @brief API for configuring and manipulating Heart Rate 10 Click driver.
     * @{
     */
    
    /**
     * @brief Heart Rate 10 configuration object setup function.
     * @details This function initializes click configuration structure to initial
     * values.
     * @param[out] cfg : Click configuration structure.
     * See #heartrate10_cfg_t object definition for detailed explanation.
     * @return Nothing.
     * @note The all used pins will be set to unconnected state.
     */
    //void MAX86916_cfg_setup ( heartrate10_cfg_t *cfg );
    
    
    
    /**
     * @brief Heart Rate 10 initialization function.
     * @details This function initializes all necessary pins and peripherals used
     * for this click board.
     * @param[out] ctx : Click context object.
     * See #heartrate10_t object definition for detailed explanation.
     * @param[in] cfg : Click configuration structure.
     * See #heartrate10_cfg_t object definition for detailed explanation.
     * @return @li @c  0 - Success,
     *         @li @c -1 - Error.
     *
     * See #err_t definition for detailed explanation.
     */
    //uint32_t MAX86916_init ( heartrate10_t *ctx, heartrate10_cfg_t *cfg );
    
    /**
     * @brief Heart Rate 10 default configuration function.
     * @details This function executes a default configuration of Heart Rate 10
     * click board.
     * @param[in] ctx : Click context object.
     * See #heartrate10_t object definition for detailed explanation.
     * @return @li @c  0 - Success,
     *         @li @c -1 - Error.
     *
     * See #err_t definition for detailed explanation.
     * @note This function can consist any necessary configuration or setting to put
     * device into operating mode.
     */
    //uint32_t MAX86916_default_cfg ( heartrate10_t *ctx );
    
    /**
     * @brief Writing function.
     * @details This function writes a data to the selected register.
     * @param[in] ctx : Click context object.
     * See #heartrate10_t object definition for detailed explanation.
     * @param[in] reg : Start register address.
     * @param[in] tx_data : Data to be written.
     * @return @li @c  0 - Success,
     *         @li @c -1 - Error.
     *
     * See #err_t definition for detailed explanation.
     */
    //uint32_t MAX86916_generic_write ( uint8_t reg, uint8_t tx_data );
    uint32_t max86916_register_write(uint8_t register_address, const uint8_t value);
    
    /**
     * @brief Reading function.
     * @details This function reads a data from the selected register.
     * @param[in] ctx : Click context object.
     * See #heartrate10_t object definition for detailed explanation.
     * @param[in] reg : Register address.
     * @param[out] rx_data : Output read data.
     * @return @li @c  0 - Success,
     *         @li @c -1 - Error.
     *
     * See #err_t definition for detailed explanation.
     */
    //uint32_t MAX86916_generic_read ( heartrate10_t *ctx, uint8_t reg, uint8_t *rx_data );
    uint32_t max86916_register_read(uint8_t register_address, uint8_t *destination, uint8_t number_of_bytes);
    
    /**
     * @brief Get interrupt pin state.
     * @details Read and return @b int_pin state.
     * @param[in] ctx : Click context object.
     * See #heartrate10_t object definition for detailed explanation.
     * @return Pin state
     *
     * See #err_t definition for detailed explanation.
     */
    //uint8_t MAX86916_get_int_pin ( );
    
    /**
     * @brief Reset device.
     * @details Set bit for reseting device and waits untill bit is cleared.
     * @param[in] ctx : Click context object.
     * See #heartrate10_t object definition for detailed explanation.
     * @return Nothing.
     */
    //void MAX86916_reset ( heartrate10_t *ctx );
    
    /**
     * @brief Read fifo register data.
     * @details This function reads a data from the @b HEARTRATE10_REG_FIFO_DATA.
     * @param[in] ctx : Click context object.
     * See #heartrate10_t object definition for detailed explanation.
     * @param[out] rx_buf : Output read data.
     * @param[in] rx_len : Length of data to read.
     * @return @li @c  0 - Success,
     *         @li @c -1 - Error.
     *
     * See #err_t definition for detailed explanation.
     */
    //uint32_t MAX86916_fifo_read ( heartrate10_t *ctx, uint8_t *rx_buf, uint8_t rx_len );
    
    /**
     * @brief Read single sample of FIFO data.
     * @details This function reads a 3 bytes from FIFO data register 
     * and concatenates data into 19bit led data.
     * @param[in] ctx : Click context object.
     * See #heartrate10_t object definition for detailed explanation.
     * @return Led data.
     * 
     * @note This function will read sample of data for four led diod it assumes 
     * that there is at least one led enabled and set in sequence register.
     */
    //uint32_t MAX86916_read_fifo_sample ( heartrate10_t *ctx );
    
    /**
     * @brief Read all led samples.
     * @details This function reads a 12 bytes from FIFO data register 
     * and concatenates data into 4 x 19bit led data.
     * @param[in] ctx : Click context object.
     * See #heartrate10_t object definition for detailed explanation.
     * @param[out] led1 : Led1 output read data.
     * @param[out] led2 : Led2 output read data.
     * @param[out] led3 : Led3 output read data.
     * @param[out] led4 : Led4 output read data.
     * @return @li @c  0 - Success,
     *         @li @c -1 - Error.
     *
     * See #err_t definition for detailed explanation.
     * @note This function will read four samples of data for four led diodes
     * it assumes that there are 4 leds enabled and set in sequence register.
     */
    
    uint32_t max86916_verify_product_id(void);
    uint32_t MAX86916_read_complete_fifo_data ( uint32_t *led1, uint32_t *led2, uint32_t *led3, uint32_t *led4 );
    
    
    
    
    //bool MPU6050_ReadGyro(int16_t *pGYRO_X , int16_t *pGYRO_Y , int16_t *pGYRO_Z );
    //bool MPU6050_ReadAcc( int16_t *pACC_X , int16_t *pACC_Y , int16_t *pACC_Z );
    
    #endif // HEARTRATE10_H
    
    /*! @} */ // heartrate10
    
    // ------------------------------------------------------------------------ END

    and C library 

    #include <stdbool.h>
    #include <stdint.h>
    #include <string.h>
    #include "nrf_drv_twi.h"
    
    #include "max86916.h"
    
    
    //Initializing TWI0 instance
    #define TWI_INSTANCE_ID     0
    
    
    // A flag to indicate the transfer state
    static volatile bool m_xfer_done = false;
    
    
    // Create a Handle for the twi communication
    static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);
    
    
    //Event Handler
    void twi_handler(nrf_drv_twi_evt_t const * p_event, void * p_context)
    {
        //Check the event to see what type of event occurred
        switch (p_event->type)
        {
            //If data transmission or receiving is finished
    	case NRF_DRV_TWI_EVT_DONE:
            m_xfer_done = true;//Set the flag
            break;
            
            default:
            // do nothing
              break;
        }
    }
    
    
    
    //Initialize the TWI as Master device
    void twi_master_init(void)
    {
        ret_code_t err_code;
    
        // Configure the settings for twi communication
        const nrf_drv_twi_config_t twi_config = {
           .scl                = TWI_SCL_M,  //SCL Pin
           .sda                = TWI_SDA_M,  //SDA Pin
           .frequency          = NRF_DRV_TWI_FREQ_100K, //Communication Speed
           .interrupt_priority = APP_IRQ_PRIORITY_HIGH, //Interrupt Priority(Note: if using Bluetooth then select priority carefully)
           .clear_bus_init     = false //automatically clear bus
        };
    
    
        //A function to initialize the twi communication
        err_code = nrf_drv_twi_init(&m_twi, &twi_config, twi_handler, NULL);
        APP_ERROR_CHECK(err_code);
        
        //Enable the TWI Communication
        nrf_drv_twi_enable(&m_twi);
    }
    
    
    
    /*
       A function to write a Single Byte to MPU6050's internal Register
    */ 
    uint32_t max86916_register_write(uint8_t register_address, uint8_t value)
    {
        ret_code_t err_code;
        uint8_t tx_buf[MAX86916_ADDRESS_LEN+1];
    	
        //Write the register address and data into transmit buffer
        tx_buf[0] = register_address;
        tx_buf[1] = value;
    
        //Set the flag to false to show the transmission is not yet completed
        m_xfer_done = false;
        
        //Transmit the data over TWI Bus
        err_code = nrf_drv_twi_tx(&m_twi, MAX86916_ADDRESS, tx_buf, MAX86916_ADDRESS_LEN+1, false);
        
        //Wait until the transmission of the data is finished
        while (m_xfer_done == false)
        {
          }
    
        // if there is no error then return true else return false
        if (NRF_SUCCESS != err_code)
        {
            return false;
        }
        
        return true;	
    }
    
    
    
    
    /*
      A Function to read data from the MPU6050
    */ 
    uint32_t max86916_register_read(uint8_t register_address, uint8_t * destination, uint8_t number_of_bytes)
    {
        ret_code_t err_code;
    
        //Set the flag to false to show the receiving is not yet completed
        m_xfer_done = false;
        
        // Send the Register address where we want to write the data
        err_code = nrf_drv_twi_tx(&m_twi, MAX86916_ADDRESS, &register_address, 1, true);
    	  
        //Wait for the transmission to get completed
        while (m_xfer_done == false){}
        
        // If transmission was not successful, exit the function with false as return value
        if (NRF_SUCCESS != err_code)
        {
            return false;
        }
    
        //set the flag again so that we can read data from the MPU6050's internal register
        m_xfer_done = false;
    	  
        // Receive the data from the MPU6050
        err_code = nrf_drv_twi_rx(&m_twi, MAX86916_ADDRESS, destination, number_of_bytes);
    		
        //wait until the transmission is completed
        while (m_xfer_done == false){}
    	
        // if data was successfully read, return true else return false
        if (NRF_SUCCESS != err_code)
        {
            return false;
        }
        
        return true;
    }
    
    
    
    /*
      A Function to verify the product id
      (its a basic test to check if we are communicating with the right slave, every type of I2C Device has 
      a special WHO_AM_I register which holds a specific value, we can read it from the MPU6050 or any device
      to confirm we are communicating with the right device)
    */ 
    uint32_t max86916_verify_product_id(void)
    {
        uint8_t who_am_i; // create a variable to hold the who am i value
    
    
        // Note: All the register addresses including WHO_AM_I are declared in 
        // MPU6050.h file, you can check these addresses and values from the
        // datasheet of your slave device.
        if (max86916_register_read(ADDRESS_WHO_AM_I, &who_am_i, 1))
        {
            if (who_am_i != MAX86916_WHO_AM_I)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        else
        {
            return false;
        }
    }
    
    
    
    /*
      Function to initialize the mpu6050
    */ 
    uint32_t max86916_init(void)
    {   
      uint32_t transfer_succeeded = true;
    	
      //Check the id to confirm that we are communicating with the right device
      transfer_succeeded &= max86916_verify_product_id();
    	
      if(max86916_verify_product_id() == false)
        {
    	return false;
          }
    
      // Set the registers with the required values, see the datasheet to get a good idea of these values
      (void)max86916_register_write(MAX86916_REG_MODE_CFG1, 0x03); 
      (void)max86916_register_write(MAX86916_REG_MODE_CFG2, 0x64); 
      (void)max86916_register_write(MAX86916_REG_LED_SEQ1, 0x21); 						
      (void)max86916_register_write(MAX86916_REG_LED_SEQ2, 0x43); 
      (void)max86916_register_write(MAX86916_REG_LED_RANGE, 0x00);
       
      (void)max86916_register_write(MAX86916_REG_LED1_PA, 0xFF);   		
     (void)max86916_register_write(MAX86916_REG_LED1_PA, 0xFF);   	
      (void)max86916_register_write(MAX86916_REG_LED1_PA, 0xFF);   	
       (void)max86916_register_write(MAX86916_REG_LED1_PA, 0xFF);  
       
         (void)max86916_register_write(MAX86916_REG_FIFO_CFG, 0x10);   	
       (void)max86916_register_write(MAX86916_REG_INT_ENABLE, 0x40); 
         	
      return transfer_succeeded;
    }
    
    
    
    
    /*
      Read the Gyro values from the MPU6050's internal Registers
    */ 
    //bool MAX86916_read_complete_fifo_data(int16_t *pGYRO_X , int16_t *pGYRO_Y , int16_t *pGYRO_Z )
    uint32_t MAX86916_read_complete_fifo_data ( uint32_t *led1, uint32_t *led2, uint32_t *led3, uint32_t *led4 )
    
    {
    uint8_t sample_parts[ 12 ] = { 0 };
    uint32_t ret = false;	
    //bool error_flag = max86916_read_complete_fifo_data( sample_parts, 12 );
      //  bool error_flag = heartrate10_fifo_read( ctx, sample_parts, 12 );
    
    if(max86916_register_read(MAX86916_OUT, sample_parts, 12) == true)
      {
        max86916_register_read(MAX86916_OUT, sample_parts, 12);
        
    
        *led1 = sample_parts[ 2 ] | ( ( uint32_t )sample_parts[ 1 ] << 8 ) | ( ( uint32_t )sample_parts[ 0 ] << 16 );
        *led1 &= 0x0007FFFF;
        *led2 = sample_parts[ 5 ] | ( ( uint32_t )sample_parts[ 4 ] << 8 ) | ( ( uint32_t )sample_parts[ 3 ] << 16 );
        *led2 &= 0x0007FFFF;
        *led3 = sample_parts[ 8 ] | ( ( uint32_t )sample_parts[ 7 ] << 8 ) | ( ( uint32_t )sample_parts[ 6 ] << 16 );
        *led3 &= 0x0007FFFF;
        *led4 = sample_parts[ 11 ] | ( ( uint32_t )sample_parts[ 10 ] << 8 ) | ( ( uint32_t )sample_parts[ 9 ] << 16 );
        *led4 &= 0x0007FFFF;
    
      ret = true;
        }
    
       return ret;
    }	
    
    
    
    
    

    and Main Code

    
    #include <stdio.h>
    #include "boards.h"
    #include "app_util_platform.h"
    
    #include "nrf_drv_twi.h"
    
    
    
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    
    #include "max86916.h"
    #include "nrf_log.h"
    #include "app_error.h"
    
    
    // main code
    
    int main(void)
    {
    
    // initialize the logger
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    	
    	
    // create arrays which will hold x,y & z co-ordinates values of acc and gyro
        static int32_t read_complete_fifo_data[4];
    
        bsp_board_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS); // initialize the leds and buttons
    
        twi_master_init(); // initialize the twi 
        nrf_delay_ms(1000); // give some delay
    
        while(max86916_init() == false) // wait until MPU6050 sensor is successfully initialized
        {
          NRF_LOG_INFO("MAX86916 initialization failed!!!"); // if it failed to initialize then print a message
          nrf_delay_ms(1000);
        }
    
       NRF_LOG_INFO("MAX86916 Init Successfully!!!"); 
    
       NRF_LOG_INFO("Reading Values FIFO"); // display a message to let the user know that the device is starting to read the values
       nrf_delay_ms(2000);
    
    
      
        
        while (true)
        {
            if(MAX86916_read_complete_fifo_data(&read_complete_fifo_data[0], &read_complete_fifo_data[1], &read_complete_fifo_data[2], &read_complete_fifo_data[3]) == true) // Read acc value from mpu6050 internal registers and save them in the array
            {
              NRF_LOG_INFO("Values FIFO:  led1 = %d  led2 = %d  led3 = %d  led4 = %d", read_complete_fifo_data[0], read_complete_fifo_data[1], read_complete_fifo_data[2], &read_complete_fifo_data[3]); // display the read values
            }
            else
            {
              NRF_LOG_INFO("Reading FIFO values Failed!!!"); // if reading was unsuccessful then let the user know about it
            }
    
    
           nrf_delay_ms(100); // give some delay 
    
    
        }
    }
    
    /** @} */
    

  • Hello, 

    I'm afraid there is not much we can do in regards to drivers from 3. party vendors. 

    What SDK are you using? If using the nRF Connect SDK, please have a look at the Zephyr sample for MAX 30101

    Kind regards,
    Øyvind

  • Thanks for the reply. I checked but page is not responsive 

  • Hello, please check again. 

    Kind regards,
    Øyvind

Related