Mahmood ul Hassan
Created October 23, 2022

thingyMonster: A Smart Kitchen Garden

Plant needs light, water and optimal environment for optimal growth. In this project we are going to automate these requirements.

74

Things used in this project

Hardware components

Nordic Thingy:53
Nordic Semiconductor Nordic Thingy:53
×1
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
×1
4-Channel 16-Bit ADC for Raspberry Pi (ADS1115)
Seeed Studio 4-Channel 16-Bit ADC for Raspberry Pi (ADS1115)
×1
Adafruit INA219 High Side DC Current Sensor Breakout
×1
Flora RGB Neopixel LEDs- Pack of 4
Adafruit Flora RGB Neopixel LEDs- Pack of 4
×1
mini Buck module
×1
Grove - 16-Channel PWM Driver (PCA9685)
Seeed Studio Grove - 16-Channel PWM Driver (PCA9685)
×1
9V 1A Switching Wall Power Supply
9V 1A Switching Wall Power Supply
9V or 12V power supply can be used
×1
J-Link EDU Mini
Any J-tag SWD with M-33 debugging functionality will work
×1
Micro Submersible Water Pump
×1
3W Grow Lights
×4
PCA9685: 16-Channel 12-bit PWM/Servo Driver
×1

Software apps and online services

nRF Connect SDK
Nordic Semiconductor nRF Connect SDK
Microsoft VS Code

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Breadboard, 170 Pin
Breadboard, 170 Pin
3D Printer (generic)
3D Printer (generic)

Story

Read more

Code

thingy53_nrf5340_cpuapp_ns.overlay

C/C++
// To get started, press Ctrl+Space to bring up the completion menu and view the available nodes.

// You can also use the buttons in the sidebar to perform actions on nodes.
// Actions currently available include:

// * Enabling / disabling the node
// * Adding the bus to a bus
// * Removing the node
// * Connecting ADC channels

// For more help, browse the DeviceTree documentation at https://docs.zephyrproject.org/latest/guides/dts/index.html
// You can also visit the nRF DeviceTree extension documentation at https://nrfconnect.github.io/vscode-nrf-connect/devicetree/nrfdevicetree.html


&i2c1 {
	compatible = "nordic,nrf-twim";
	status = "okay";
	clock-frequency = <I2C_BITRATE_STANDARD>;

	pinctrl-0 = <&i2c1_default>;
	pinctrl-1 = <&i2c1_sleep>;
	pinctrl-names = "default", "sleep";

    bh1749: bh1749@38 {
		compatible = "rohm,bh1749";
        label = "BH1749";
		reg = <0x38>;
		int-gpios = <&gpio1 5 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
	};
	bme680: bme680@76 {
		compatible = "bosch,bme680";
		reg = <0x76>;
		label = "BME680";
	};
};

&pinctrl {
	i2c2_default: i2c2_default {
		group1 {
			psels = <NRF_PSEL(TWIM_SDA, 0, 4)>,
				<NRF_PSEL(TWIM_SCL, 0, 5)>;
		};
	};

	i2c2_sleep: i2c2_sleep {
		group1 {
			psels = <NRF_PSEL(TWIM_SDA, 0, 4)>,
				<NRF_PSEL(TWIM_SCL, 0, 5)>;
			low-power-enable;
		};
	};
};

&i2c2 {
	compatible = "nordic,nrf-twim";
	status = "okay";
	clock-frequency = <I2C_BITRATE_STANDARD>;

	pinctrl-0 = <&i2c2_default>;
	pinctrl-1 = <&i2c2_sleep>;
	pinctrl-names = "default", "sleep";

	ina219: ina219@41 {
		status = "okay";
		compatible = "ti,ina219";
		reg = <0x41>;
		brng = <0>;
		pg = <0>;
		sadc = <13>;
		badc = <13>;
		shunt-milliohm = <100>;
		lsb-microamp = <10>;
	};
	ads1115: ads1115@48 {
		compatible = "ti,ads1115";
		#io-channel-cells = <1>;
		reg = <0x48 >;
		label = "ADS1115";
	};
	pca9685: pca9685@40 {
		compatible = "i2c-device";
		reg = <0x40 >;
		label = "PCA9685";
	};
	ssd1306: ssd1306@3C {
		compatible = "i2c-device";
		reg = <0x3C >;
		label = "SSD1306";
	};
};

main.c

C/C++
/*
 * Copyright (c) 2012-2014 Wind River Systems, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "main.h"
#include "bh1749.h"
#include "ads1115.h"
#include "pca9685.h"
#include "ssd1306.h"
#include "binary.h"

#include "neopixel.h"


BH_VALUES bh_val;
ADS1115 ads1115;
PCA9685 pca9685;

static const struct device *bme = DEVICE_DT_GET_ONE(bosch_bme680);
static struct sensor_value temp, press, humidity, gas_res;
static uint8_t init_bme680(void);

static const struct device *ina = DEVICE_DT_GET_ONE(ti_ina219);
static struct sensor_value v_bus, power, current;
static uint8_t init_ina219(void);

void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
	printk("Button pressed at %" PRIu32 "\n", k_cycle_get_32());
}

void main(void)
{
	int ret;
	if (IS_ENABLED(CONFIG_LOG_BACKEND_RTT)) {
		/* Give RTT log time to be flushed before executing tests */
		k_sleep(K_MSEC(500));
	}

	HAL_delay(3000);

	init_bh1749();
	init_bme680();
	init_ina219();

	ADS1115_init(&ads1115);

	pca9685_getconfig(&pca9685);
	pca9685_init(&pca9685);

	neopixel_init(5);

    ssd1306_begin(SSD1306_SWITCHCAPVCC, false);
	ssd1306_clear_display();
	ssd1306_display();

	HAL_delay(2000);

	ssd1306_draw_circle(SSD1306_LCDWIDTH / 2, SSD1306_LCDHEIGHT / 2, 30, WHITE);
	ssd1306_display();
	HAL_delay(1000);

	float soil_moisture[4]={0};

	uint8_t rgb_pixel=0;
	while(1){
		HAL_delay(1000);
/*
		for (uint8_t pin=0; pin<16; pin++) {
			pca9685_setPWM(&pca9685, pin, 4096, 0);       // turns pin fully on
			HAL_delay(100);
			pca9685_setPWM(&pca9685, pin, 0, 4096);       // turns pin fully off
			HAL_delay(50);
		}
*/		
		for(int i=0; i<5; i++){
			neopixel_set_color((rgb_pixel + 0)%5, 255, 0, 0);
			neopixel_set_color((rgb_pixel + 1)%5, 0, 255, 0);
			neopixel_set_color((rgb_pixel + 2)%5, 0, 0, 255);
			neopixel_set_color((rgb_pixel + 3)%5, 255, 0, 0);
			neopixel_set_color((rgb_pixel + 4)%5, 0, 255, 0);
			neopixel_show();
		}

		rgb_pixel++;

		bh1479_read_values(&bh_val);
			//Print reading to console  
		printk("______________________________________\n");
		printk("Red Value:\t %d\n", bh_val.bh_red);
		printk("Green Value:\t %d\n", bh_val.bh_green);
		printk("Blue Value:\t %d\n", bh_val.bh_blue);
		printk("IR Value:\t %d\n", bh_val.bh_ir);

		soil_moisture[0] = ADS1115_readADC(&ads1115, CH_0);
		soil_moisture[1] = ADS1115_readADC(&ads1115, CH_1);
		soil_moisture[2] = ADS1115_readADC(&ads1115, CH_2);
		soil_moisture[3] = ADS1115_readADC(&ads1115, CH_3);

		printf("ADC_0: %0.2f | ADC_1: %0.2f | ADC_2: %0.2f | ADC_3: %0.2f\n", 
				soil_moisture[0], 
				soil_moisture[1], 
				soil_moisture[2], 
				soil_moisture[3]);


		ret = sensor_sample_fetch(bme);
		if (ret) {
			printk("Could not fetch bme680 data.\n");
			return;
		}
		sensor_channel_get(bme, SENSOR_CHAN_AMBIENT_TEMP, &temp);
		sensor_channel_get(bme, SENSOR_CHAN_PRESS, &press);
		sensor_channel_get(bme, SENSOR_CHAN_HUMIDITY, &humidity);
		sensor_channel_get(bme, SENSOR_CHAN_GAS_RES, &gas_res);

		printk("T: %d.%06d | P: %d.%06d | H: %d.%06d | G: %d.%06d\n",
				temp.val1, temp.val2, press.val1, press.val2,
				humidity.val1, humidity.val2, gas_res.val1,
				gas_res.val2);

		ret = sensor_sample_fetch(ina);
		if (ret) {
			printk("Could not fetch ina219 data.\n");
			return;
		}
		sensor_channel_get(ina, SENSOR_CHAN_VOLTAGE, &v_bus);
		sensor_channel_get(ina, SENSOR_CHAN_POWER, &power);
		sensor_channel_get(ina, SENSOR_CHAN_CURRENT, &current);

		printf("Bus: %f [V] | Power: %f [W] | Current: %f [A]\n\n", 
				sensor_value_to_double(&v_bus), 
				sensor_value_to_double(&power), 
				sensor_value_to_double(&current));

		if(soil_moisture[0]>2 || soil_moisture[1]>2 || soil_moisture[2]>2 || soil_moisture[3]>2 ){
			pca9685_setPWM(&pca9685, 1, 4096, 0);
		}
		else{
			pca9685_setPWM(&pca9685, 1, 0, 4096);       // turns pin fully off
		}

		uint16_t light_intensity = bh_val.bh_red+bh_val.bh_green+bh_val.bh_blue;
		if(light_intensity < 500 ){
			pca9685_setPWM(&pca9685, 0, 4096, light_intensity*8);
		}
		else{
			pca9685_setPWM(&pca9685, 0, 0, 4096);       // turns pin fully off
		}

	}
}

static uint8_t init_bme680(void){
	if (!device_is_ready(bme)) {
		printk("sensor: device not ready.\n");
		return 0;
	}
	printk("Device %p name is %s\n", bme, bme->name);
	return 1;
}

static uint8_t init_ina219(void){
	if (!device_is_ready(ina)) {
		printk("Device %s is not ready.\n", ina->name);
		return 0;
	}
	printk("Device %p name is %s\n", ina, ina->name);
	return 1;
}

neopixel.c

C/C++
#include "neopixel.h"

#include "main.h"


static uint8_t mPIXEL_DATA_PIN;
static neopixel_strip_t m_strip;


#define HAL_nsDelay(nano_sec) k_sleep(K_NSEC(nano_sec))
#define HAL_usDelay(micro_sec) k_sleep(K_USEC(micro_sec))
#define HAL_msDelay(mili_sec) k_sleep(K_MSEC(mili_sec))



#define NEOPIXEL_SEND_ONE gpio_port_set_bits_raw(led_neopixel.port, (gpio_port_pins_t)BIT(led_neopixel.pin)); \
__ASM ( \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
" NOP\n\t" \
); \
gpio_port_clear_bits_raw(led_neopixel.port, (gpio_port_pins_t)BIT(led_neopixel.pin)); \
__ASM ( \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
); //Logic "1" 52x1 + 50x0 * 15.625ns = 812.5ns H + 781.25ns L

#define NEOPIXEL_SEND_ZERO gpio_port_set_bits_raw(led_neopixel.port, (gpio_port_pins_t)BIT(led_neopixel.pin)); \
__ASM ( \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
); \
gpio_port_clear_bits_raw(led_neopixel.port, (gpio_port_pins_t)BIT(led_neopixel.pin)); \
__ASM ( \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
" NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t NOP\n\t" \
); //Logic "0" 2x1 + 40x0 * 15.625ns = 30.25+overhead ns H + 781.25ns L

/*
#define NEOPIXEL_SEND_ONE 	gpio_port_set_bits_raw(led_neopixel.port, (gpio_port_pins_t)BIT(led_neopixel.pin)); \
							HAL_nsDelay(400); \
							gpio_port_clear_bits_raw(led_neopixel.port, (gpio_port_pins_t)BIT(led_neopixel.pin)); \
							HAL_nsDelay(850);

#define NEOPIXEL_SEND_ZERO 	gpio_port_set_bits_raw(led_neopixel.port, (gpio_port_pins_t)BIT(led_neopixel.pin)); \
							HAL_nsDelay(800); \
							gpio_port_clear_bits_raw(led_neopixel.port, (gpio_port_pins_t)BIT(led_neopixel.pin)); \
							HAL_nsDelay(450);
*/
#define NEOPIXEL_SET_ONE(void)		gpio_port_set_bits_raw(led_neopixel.port, (gpio_port_pins_t)BIT(led_neopixel.pin))

#define NEOPIXEL_SET_ZERO(void) 	gpio_port_clear_bits_raw(led_neopixel.port, (gpio_port_pins_t)BIT(led_neopixel.pin))


void neopixel_init(uint16_t num_leds)
{
	if (!device_is_ready(led_neopixel.port)) {
		printk("RGB LED port ERROR\n");
	}else{
		printk("RGB LED port OK\n");
	}

	if (!gpio_pin_configure_dt(&led_neopixel, GPIO_OUTPUT_INACTIVE)) {
		printk("RGB LED config LOW: OK\n");
	}else{
		printk("RGB LED config ERROR\n");
	}
	if (!gpio_pin_configure_dt(&led_neopixel, GPIO_OUTPUT_ACTIVE)) {
		printk("RGB LED config HIGH: OK\n");
	}else{
		printk("RGB LED config ERROR\n");
	}

	mPIXEL_DATA_PIN=led_neopixel.pin;
	m_strip.leds = (color_t*) malloc(sizeof(color_t) * num_leds);
	m_strip.pin_num = mPIXEL_DATA_PIN;
	m_strip.num_leds = num_leds;

	for (int i = 0; i < num_leds; i++)
	{	
		m_strip.leds[i].simple.g = 0;
		m_strip.leds[i].simple.r = 0;
		m_strip.leds[i].simple.b = 0;
	}

}

void neopixel_clear()
{
	for (int i = 0; i < m_strip.num_leds; i++)
	{
		m_strip.leds[i].simple.g = 0;
		m_strip.leds[i].simple.r = 0;
		m_strip.leds[i].simple.b = 0;
	}
	neopixel_show();
}

void neopixel_show()
{

	NEOPIXEL_SET_ZERO();
	HAL_usDelay(50);
	__disable_irq();
	for (int i = 0; i < m_strip.num_leds; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			if ((m_strip.leds[i].grb[j] & 128) > 0)	{NEOPIXEL_SEND_ONE}
			else	{NEOPIXEL_SEND_ZERO}
			
			if ((m_strip.leds[i].grb[j] & 64) > 0)	{NEOPIXEL_SEND_ONE}
			else	{NEOPIXEL_SEND_ZERO}
			
			if ((m_strip.leds[i].grb[j] & 32) > 0)	{NEOPIXEL_SEND_ONE}
			else	{NEOPIXEL_SEND_ZERO}
			
			if ((m_strip.leds[i].grb[j] & 16) > 0)	{NEOPIXEL_SEND_ONE}
			else	{NEOPIXEL_SEND_ZERO}
			
			if ((m_strip.leds[i].grb[j] & 8) > 0)	{NEOPIXEL_SEND_ONE}
			else	{NEOPIXEL_SEND_ZERO}
			
			if ((m_strip.leds[i].grb[j] & 4) > 0)	{NEOPIXEL_SEND_ONE}
			else	{NEOPIXEL_SEND_ZERO}
			
			if ((m_strip.leds[i].grb[j] & 2) > 0)	{NEOPIXEL_SEND_ONE}
			else	{NEOPIXEL_SEND_ZERO}
			
			if ((m_strip.leds[i].grb[j] & 1) > 0)	{NEOPIXEL_SEND_ONE}
			else	{NEOPIXEL_SEND_ZERO}
		}
	}

	HAL_usDelay(50);
	__enable_irq();
}

void neopixel_set_color(uint16_t index, uint8_t red, uint8_t green, uint8_t blue )
{
	m_strip.leds[index].simple.r = red;
	m_strip.leds[index].simple.g = green;
	m_strip.leds[index].simple.b = blue;
}

void neopixel_set_color_and_show(uint16_t index, uint8_t red, uint8_t green, uint8_t blue)
{
	m_strip.leds[index].simple.r = red;
	m_strip.leds[index].simple.g = green;
	m_strip.leds[index].simple.b = blue;
	neopixel_show();
}

void neopixel_destroy()
{
	free(m_strip.leds);
	m_strip.num_leds = 0;
	m_strip.pin_num = 0;
}

neopixel.h

C/C++
#ifndef NEOPIXEL_H
 #define NEOPIXEL_H


#include <stdbool.h>
#include <stdint.h>

typedef union {
	struct {
		uint8_t g, r, b;
	}simple;
	uint8_t grb[3];
} color_t;
typedef struct {
	uint8_t pin_num;
	uint16_t num_leds;
	color_t *leds;
} neopixel_strip_t;

void neopixel_init(uint16_t num_leds);
void neopixel_clear();
void neopixel_show();
void neopixel_set_color(uint16_t index, uint8_t red, uint8_t green, uint8_t blue );
void neopixel_set_color_and_show(uint16_t index, uint8_t red, uint8_t green, uint8_t blue);
void neopixel_destroy();

#endif

pca9685.c

C/C++
/*!
 *  @file Adafruit_PWMServoDriver.cpp
 *
 *  @mainpage Adafruit 16-channel PWM & Servo driver
 *
 *  @section intro_sec Introduction
 *
 *  This is a library for the 16-channel PWM & Servo driver.
 *
 *  Designed specifically to work with the Adafruit PWM & Servo driver.
 *
 *  Pick one up today in the adafruit shop!
 *  ------> https://www.adafruit.com/product/815
 *
 *  These displays use I2C to communicate, 2 pins are required to interface.
 *
 *  Adafruit invests time and resources providing this open source code,
 *  please support Adafruit andopen-source hardware by purchasing products
 *  from Adafruit!
 *
 *  @section author Author
 *
 *  Limor Fried/Ladyada (Adafruit Industries).
 *
 *  @section license License
 *
 *  BSD license, all text above must be included in any redistribution
 */

#include "pca9685.h"
#include "main.h"
#include <stdio.h>
#include <string.h>

uint8_t pca9685_txBuff[8];
uint8_t pca9685_rxBuff[8];
//#define ENABLE_DEBUG_OUTPUT
static uint8_t pca9685_read8(PCA9685 *pca9685_module, uint8_t addr);
static void pca9685_write8(PCA9685 *pca9685_module, uint8_t addr, uint8_t d);


/*!
 *  @brief  Setups the I2C interface and hardware
 *  @param  prescale
 *          Sets External Clock (Optional)
 */
void pca9685_getconfig(PCA9685 *pca9685_module){
	pca9685_module->_i2caddr = PCA9685_I2C_ADDRESS;
	pca9685_module->_prescaler = 0;
	pca9685_module->_pwmfeq = 1000;
	pca9685_module->_clksrc = PCA9685_intclk;
	pca9685_module->_oscillator_freq = FREQUENCY_OSCILLATOR;
}


/*!
 *  @brief  Setups the I2C interface and hardware
 *  @param  prescale
 *          Sets External Clock (Optional)
 */
void pca9685_init(PCA9685 *pca9685_module) {
	pca9685_reset(pca9685_module);

	if (pca9685_module->_prescaler) {
		pca9685_setExtClk(pca9685_module);
	} else {
		// set a default frequency
		pca9685_setPWMFreq(pca9685_module);
	}
}

/*!
 *  @brief  Sends a reset command to the PCA9685 chip over I2C
 */
void pca9685_reset(PCA9685 *pca9685_module) {
	pca9685_write8(pca9685_module, PCA9685_MODE1, MODE1_RESTART);
	HAL_delay(10);
}

/*!
 *  @brief  Puts board into sleep mode
 */
void pca9685_sleep(PCA9685 *pca9685_module) {
	uint8_t awake = pca9685_read8(pca9685_module, PCA9685_MODE1);
	uint8_t sleep = awake | MODE1_SLEEP; // set sleep bit high
	pca9685_write8(pca9685_module, PCA9685_MODE1, sleep);
	HAL_delay(5); // wait until cycle ends for sleep to be active
}

/*!
 *  @brief  Wakes board from sleep
 */
void pca9685_wakeup(PCA9685 *pca9685_module) {
  uint8_t sleep = pca9685_read8(pca9685_module, PCA9685_MODE1);
  uint8_t wakeup = sleep & ~MODE1_SLEEP; // set sleep bit low
  pca9685_write8(pca9685_module, PCA9685_MODE1, wakeup);
}

/*!
 *  @brief  Sets EXTCLK pin to use the external clock
 *  @param  prescale
 *          Configures the prescale value to be used by the external clock
 */
void pca9685_setExtClk(PCA9685 *pca9685_module) {
  uint8_t oldmode = pca9685_read8(pca9685_module, PCA9685_MODE1);
  uint8_t newmode = (oldmode & ~MODE1_RESTART) | MODE1_SLEEP; // sleep
  pca9685_write8(pca9685_module, PCA9685_MODE1, newmode); // go to sleep, turn off internal oscillator

  // This sets both the SLEEP and EXTCLK bits of the MODE1 register to switch to
  // use the external clock.
  pca9685_write8(pca9685_module, PCA9685_MODE1, (newmode |= MODE1_EXTCLK));

  pca9685_write8(pca9685_module, PCA9685_PRESCALE, pca9685_module->_prescaler); // set the prescaler

  HAL_delay(5);
  // clear the SLEEP bit to start
  pca9685_write8(pca9685_module, PCA9685_MODE1, (newmode & ~MODE1_SLEEP) | MODE1_RESTART | MODE1_AI);

#ifdef ENABLE_DEBUG_OUTPUT
  PRINTF("Mode now 0x%02X\n", pca9685_read8(pca9685_module, PCA9685_MODE1));
#endif
}

/*!
 *  @brief  Sets the PWM frequency for the entire chip, up to ~1.6 KHz
 *  @param  freq Floating point frequency that we will attempt to match
 */
void pca9685_setPWMFreq(PCA9685 *pca9685_module) {
#ifdef ENABLE_DEBUG_OUTPUT
  PRINTF("Attempting to set freq %ul\n", pca9685_module->_pwmfeq);
#endif
  // Range output modulation frequency is dependant on oscillator
  if (pca9685_module->_pwmfeq < 1)
	pca9685_module->_pwmfeq = 1;
  if (pca9685_module->_pwmfeq > 3500)
	pca9685_module->_pwmfeq = 3500; // Datasheet limit is 3052=50MHz/(4*4096)

  float prescaleval = ((pca9685_module->_oscillator_freq / (pca9685_module->_pwmfeq * 4096.0)) + 0.5) - 1;
  if (prescaleval < PCA9685_PRESCALE_MIN)
    prescaleval = PCA9685_PRESCALE_MIN;
  if (prescaleval > PCA9685_PRESCALE_MAX)
    prescaleval = PCA9685_PRESCALE_MAX;
  pca9685_module->_prescaler = (uint8_t)prescaleval;


#ifdef ENABLE_DEBUG_OUTPUT
  PRINTF("Final pre-scale: 0x%02X\n", pca9685_module->_prescaler);
#endif

  uint8_t oldmode = pca9685_read8(pca9685_module, PCA9685_MODE1);
  uint8_t newmode = (oldmode & ~MODE1_RESTART) | MODE1_SLEEP; // sleep
  pca9685_write8(pca9685_module, PCA9685_MODE1, newmode);                             // go to sleep
  pca9685_write8(pca9685_module, PCA9685_PRESCALE, pca9685_module->_prescaler); // set the prescaler
  pca9685_write8(pca9685_module, PCA9685_MODE1, oldmode);
  HAL_delay(5);
  // This sets the MODE1 register to turn on auto increment.
  pca9685_write8(pca9685_module, PCA9685_MODE1, oldmode | MODE1_RESTART | MODE1_AI);

#ifdef ENABLE_DEBUG_OUTPUT
  PRINTF("Mode now 0x%02X\n", pca9685_read8(pca9685_module, PCA9685_MODE1));
#endif
}

/*!
 *  @brief  Sets the output mode of the PCA9685 to either
 *  open drain or push pull / totempole.
 *  Warning: LEDs with integrated zener diodes should
 *  only be driven in open drain mode.
 *  @param  totempole Totempole if true, open drain if false.
 */
void pca9685_setOutputMode(PCA9685 *pca9685_module, bool totempole) {
  uint8_t oldmode = pca9685_read8(pca9685_module, PCA9685_MODE2);
  uint8_t newmode;
  if (totempole) {
    newmode = oldmode | MODE2_OUTDRV;
  } else {
    newmode = oldmode & ~MODE2_OUTDRV;
  }
  pca9685_write8(pca9685_module, PCA9685_MODE2, newmode);
#ifdef ENABLE_DEBUG_OUTPUT
  PRINTF("Setting output mode: %s by setting MODE2 to 0x%02X\n", (totempole) ? "totempole" : "open drain", newmode);
#endif
}

/*!
 *  @brief  Reads set Prescale from PCA9685
 *  @return prescale value
 */
uint8_t pca9685_readPrescale(PCA9685 *pca9685_module) {
  return pca9685_read8(pca9685_module, PCA9685_PRESCALE);
}

/*!
 *  @brief  Gets the PWM output of one of the PCA9685 pins
 *  @param  num One of the PWM output pins, from 0 to 15
 *  @return requested PWM output value
 */
uint8_t* pca9685_getPWM(PCA9685 *pca9685_module, uint8_t num) {
  pca9685_txBuff[0] = (PCA9685_LED0_ON_L + 4 * num);
  memset(pca9685_rxBuff, 0, 4);

  i2c_write_dt(&pca_i2c,pca9685_txBuff,1);
  i2c_read_dt(&pca_i2c,pca9685_rxBuff,4);

  return pca9685_rxBuff;
}

/*!
 *  @brief  Sets the PWM output of one of the PCA9685 pins
 *  @param  num One of the PWM output pins, from 0 to 15
 *  @param  on At what point in the 4096-part cycle to turn the PWM output ON
 *  @param  off At what point in the 4096-part cycle to turn the PWM output OFF
 */
void pca9685_setPWM(PCA9685 *pca9685_module, uint8_t num, uint16_t on, uint16_t off) {
#ifdef ENABLE_DEBUG_OUTPUT
  PRINTF("Setting PWM %d : %d -> %d\n", num, on, off);
#endif
  pca9685_txBuff[0] = PCA9685_LED0_ON_L + 4 * num;
  pca9685_txBuff[1] = on;
  pca9685_txBuff[2] = on>>8;
  pca9685_txBuff[3] = off;
  pca9685_txBuff[4] = off>>8;

 i2c_write_dt(&pca_i2c,pca9685_txBuff,5);
}

/*!
 *   @brief  Helper to set pin PWM output. Sets pin without having to deal with
 * on/off tick placement and properly handles a zero value as completely off and
 * 4095 as completely on.  Optional invert parameter supports inverting the
 * pulse for sinking to ground.
 *   @param  num One of the PWM output pins, from 0 to 15
 *   @param  val The number of ticks out of 4096 to be active, should be a value
 * from 0 to 4095 inclusive.
 *   @param  invert If true, inverts the output, defaults to 'false'
 */
void pca9685_setPin(PCA9685 *pca9685_module, uint8_t num, uint16_t val, bool invert) {
  // Clamp value between 0 and 4095 inclusive.
  val = (val < (uint16_t)4095)? val: 4095;
  if (invert) {
    if (val == 0) {
      // Special value for signal fully on.
    	pca9685_setPWM(pca9685_module, num, 4095, 0);
    } else if (val == 4095) {
      // Special value for signal fully off.
    	pca9685_setPWM(pca9685_module, num, 0, 4095);
    } else {
    	pca9685_setPWM(pca9685_module, num, 0, 4095 - val);
    }
  } else {
    if (val == 4095) {
      // Special value for signal fully on.
    	pca9685_setPWM(pca9685_module, num, 4095, 0);
    } else if (val == 0) {
      // Special value for signal fully off.
    	pca9685_setPWM(pca9685_module, num, 0, 4095);
    } else {
    	pca9685_setPWM(pca9685_module, num, 0, val);
    }
  }
}

/*!
 *  @brief  Sets the PWM output of one of the PCA9685 pins based on the input
 * microseconds, output is not precise
 *  @param  num One of the PWM output pins, from 0 to 15
 *  @param  Microseconds The number of Microseconds to turn the PWM output ON
 */
void pca9685_writeMicroseconds(PCA9685 *pca9685_module, uint8_t num,
                                                uint16_t Microseconds) {
#ifdef ENABLE_DEBUG_OUTPUT
  PRINTF("Setting PWM Via Microseconds on output %d : %d -> \n", num, Microseconds);
#endif

  double pulse = Microseconds;
  double pulselength;
  pulselength = 1000000; // 1,000,000 us per second

  // Read prescale
  uint16_t prescale = pca9685_readPrescale(pca9685_module);

#ifdef ENABLE_DEBUG_OUTPUT
  PRINTF("0x%02X PCA9685 chip prescale\n", prescale);
#endif

  // Calculate the pulse for PWM based on Equation 1 from the datasheet section
  // 7.3.5
  prescale += 1;
  pulselength *= prescale;
  pulselength /= pca9685_module->_oscillator_freq;

#ifdef ENABLE_DEBUG_OUTPUT
  PRINTF("%02f  us per bit\n", pulselength);
#endif

  pulse /= pulselength;

#ifdef ENABLE_DEBUG_OUTPUT
  PRINTF("%02f  pulse for PWM\n", pulse);
#endif

  pca9685_setPWM(pca9685_module, num, 0, pulse);
}

/*!
 *  @brief  Getter for the internally tracked oscillator used for freq
 * calculations
 *  @returns The frequency the PCA9685 thinks it is running at (it cannot
 * introspect)
 */
uint32_t pca9685_getOscillatorFrequency(PCA9685 *pca9685_module) {
  return pca9685_module->_oscillator_freq;
}

/*!
 *  @brief Setter for the internally tracked oscillator used for freq
 * calculations
 *  @param freq The frequency the PCA9685 should use for frequency calculations
 */
void pca9685_setOscillatorFrequency(PCA9685 *pca9685_module, uint32_t freq) {
  pca9685_module->_oscillator_freq = freq;
}

/******************* Low level I2C interface */

uint8_t pca9685_read8(PCA9685 *pca9685_module, uint8_t addr) {
  pca9685_txBuff[0] = addr;
  memset(pca9685_rxBuff, 0, 1);

  i2c_write_dt(&pca_i2c,pca9685_txBuff,1);
  i2c_read_dt(&pca_i2c,pca9685_rxBuff,1);

  return pca9685_rxBuff[0];
}

void pca9685_write8(PCA9685 *pca9685_module, uint8_t addr, uint8_t d) {
  pca9685_txBuff[0] = addr;
  pca9685_txBuff[1] = d;

  i2c_write_dt(&pca_i2c,pca9685_txBuff,2);
}

pca9685.h

C/C++
/*!
 *  @file pca9685.h
 *
 */

#ifndef _PCA9685_H
#define _PCA9685_H

#include <stdint.h>
#include <stdbool.h>

// REGISTER ADDRESSES
#define PCA9685_MODE1		0x00    /**< Mode Register 1 */
#define PCA9685_MODE2 		0x01    /**< Mode Register 2 */
#define PCA9685_SUBADR1 	0x02    /**< I2C-bus subaddress 1 */
#define PCA9685_SUBADR2 	0x03    /**< I2C-bus subaddress 2 */
#define PCA9685_SUBADR3 	0x04    /**< I2C-bus subaddress 3 */
#define PCA9685_ALLCALLADR 	0x05 	/**< LED All Call I2C-bus address */
#define PCA9685_LED0_ON_L 	0x06  	/**< LED0 on tick, low byte*/
#define PCA9685_LED0_ON_H 	0x07  	/**< LED0 on tick, high byte*/
#define PCA9685_LED0_OFF_L 	0x08 	/**< LED0 off tick, low byte */
#define PCA9685_LED0_OFF_H 	0x09 	/**< LED0 off tick, high byte */
// etc all 16:  LED15_OFF_H 0x45
#define PCA9685_ALLLED_ON_L 0xFA  	/**< load all the LEDn_ON registers, low */
#define PCA9685_ALLLED_ON_H 0xFB  	/**< load all the LEDn_ON registers, high */
#define PCA9685_ALLLED_OFF_L 0xFC 	/**< load all the LEDn_OFF registers, low */
#define PCA9685_ALLLED_OFF_H 0xFD 	/**< load all the LEDn_OFF registers,high */
#define PCA9685_PRESCALE 	0xFE    /**< Prescaler for PWM output frequency */
#define PCA9685_TESTMODE 	0xFF    /**< defines the test mode to be entered */

// MODE1 bits
#define MODE1_ALLCAL 	0x01  	/**< respond to LED All Call I2C-bus address */
#define MODE1_SUB3 		0x02  	/**< respond to I2C-bus subaddress 3 */
#define MODE1_SUB2 		0x04  	/**< respond to I2C-bus subaddress 2 */
#define MODE1_SUB1 		0x08  	/**< respond to I2C-bus subaddress 1 */
#define MODE1_SLEEP 	0x10  	/**< Low power mode. Oscillator off */
#define MODE1_AI 		0x20  	/**< Auto-Increment enabled */
#define MODE1_EXTCLK 	0x40  	/**< Use EXTCLK pin clock */
#define MODE1_RESTART 	0x80	/**< Restart enabled */
// MODE2 bits
#define MODE2_OUTNE_0 	0x01 	/**< Active LOW output enable input */
#define MODE2_OUTNE_1                                                          \
  0x02 /**< Active LOW output enable input - high impedience */
#define MODE2_OUTDRV 	0x04 	/**< totem pole structure vs open-drain */
#define MODE2_OCH 		0x08    /**< Outputs change on ACK vs STOP */
#define MODE2_INVRT 	0x10  	/**< Output logic state inverted */

#define PCA9685_I2C_ADDRESS 	0x40		/**< Default PCA9685 I2C Slave Address */
#define FREQUENCY_OSCILLATOR 	25000000 	/**< Int. osc. frequency in datasheet */

#define PCA9685_PRESCALE_MIN 	3   	/**< minimum prescale value */
#define PCA9685_PRESCALE_MAX 	255 	/**< maximum prescale value */

typedef enum PCA9685_clk {
  PCA9685_intclk = 0,
  PCA9685_extclk
} PCA9685_clk;

typedef struct PCA9685 {
	uint8_t _i2caddr;
	uint8_t _prescaler;
	uint16_t _pwmfeq;
	PCA9685_clk _clksrc;
	uint32_t _oscillator_freq;
} PCA9685;

void pca9685_getconfig(PCA9685 *pca9685_module);
void pca9685_init(PCA9685 *pca9685_module);
void pca9685_reset(PCA9685 *pca9685_module);
void pca9685_sleep(PCA9685 *pca9685_module);
void pca9685_wakeup(PCA9685 *pca9685_module);
void pca9685_setExtClk(PCA9685 *pca9685_module);
void pca9685_setPWMFreq(PCA9685 *pca9685_module);
void pca9685_setOutputMode(PCA9685 *pca9685_module, bool totempole);
uint8_t* pca9685_getPWM(PCA9685 *pca9685_module, uint8_t num);
void pca9685_setPWM(PCA9685 *pca9685_module, uint8_t num, uint16_t on, uint16_t off);
void pca9685_setPin(PCA9685 *pca9685_module, uint8_t num, uint16_t val, bool invert /*= false*/);
uint8_t pca9685_readPrescale(PCA9685 *pca9685_module);
void pca9685_writeMicroseconds(PCA9685 *pca9685_module, uint8_t num, uint16_t Microseconds);

void pca9685_setOscillatorFrequency(PCA9685 *pca9685_module, uint32_t freq);
uint32_t pca9685_getOscillatorFrequency(PCA9685 *pca9685_module);

#endif

ssd1306.c

C/C++
#include "binary.h"
#include "ssd1306.h"

#include "main.h"

static uint8_t _vccstate;
static int16_t _width, _height, WIDTH, HEIGHT, cursor_x, cursor_y;
static uint8_t textsize, rotation;
static uint16_t textcolor, textbgcolor;
bool wrap,   // If set, 'wrap' text at right edge of display
     _cp437; // If set, use correct CP437 charset (default is off)

#include "glcdfont.c"

// the memory buffer for the LCD

static uint8_t buffer[SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH / 8] = {


          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xD0, 0x00, 0x00, 0xC0,
          0x80, 0x40, 0x40, 0x80, 0x80, 0x40, 0x40, 0x80, 0x00, 0x00, 0xC0, 0x80, 0x40, 0x40, 0x80, 0x80,
          0x40, 0x40, 0x80, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x70, 0x80, 0x00, 0x00,
          0xE0, 0x10, 0xE0, 0x00, 0x00, 0xC0, 0x30, 0x00, 0x80, 0x40, 0x40, 0x40, 0x80, 0x00, 0x00, 0xC0,
          0x80, 0x40, 0x40, 0x80, 0x00, 0x00, 0x80, 0x40, 0x40, 0x80, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
          0xF0, 0x10, 0x10, 0x10, 0x10, 0xE0, 0x00, 0x00, 0xF0, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x10, 0x10, 0x10, 0x0F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F,
          0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1F, 0x00,
          0x00, 0x00, 0x1F, 0x00, 0x00, 0x47, 0x38, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x07,
          0x00, 0x00, 0x00, 0x07, 0x18, 0x07, 0x00, 0x00, 0x0F, 0x10, 0x10, 0x10, 0x0F, 0x00, 0x00, 0x1F,

          #if (SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH > 96*16)
          0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x4F, 0x50, 0x50, 0x48, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x1F, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x1F, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x10, 0x08,
          0x08, 0x08, 0x10, 0x20, 0x00, 0x00, 0xF8, 0x08, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x00,
          0x00, 0x00, 0xF8, 0x08, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0xC0, 0x20, 0x20, 0x20, 0xC0,
          0x00, 0x00, 0xE0, 0x40, 0x20, 0x20, 0xC0, 0x40, 0x20, 0x20, 0xC0, 0x00, 0x00, 0xC0, 0x20, 0x20,
          0x20, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x10, 0x90, 0x48, 0x48, 0x88, 0xC8, 0x10,
          0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x40, 0x20, 0x20, 0xC0, 0x00, 0x00, 0xF8, 0x88, 0x88,
          0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0xF8, 0x88, 0x88, 0x88, 0x08, 0x00, 0x00, 0xE0, 0x58, 0x48,
          0x48, 0x88, 0x00, 0x00, 0x10, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x03, 0x04, 0x08,
          0x08, 0x08, 0x04, 0x02, 0x00, 0x00, 0x0F, 0x08, 0x08, 0x08, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x0F, 0x08, 0x08, 0x08, 0x08, 0x04, 0x03, 0x00, 0x00, 0x07, 0x09, 0x09, 0x09, 0x05,
          0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x07, 0x08, 0x08,
          0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x10, 0x27, 0x48, 0x48, 0x48, 0x4F, 0x48, 0x28,
          0x24, 0x13, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x00,
          0x00, 0x01, 0x06, 0x08, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x08,
          0x08, 0x07, 0x00, 0x00, 0x08, 0x0C, 0x0A, 0x09, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          #if (SSD1306_LCDHEIGHT == 64)
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x80, 0x40, 0x20, 0x20, 0x20, 0x40, 0x80, 0x00, 0x00, 0xA0, 0x00, 0x80, 0xE0, 0x80,
          0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
          0x00, 0xE0, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x80, 0x00,
          0x80, 0x80, 0x00, 0x00, 0x80, 0xC0, 0xA0, 0xA0, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
          0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00,
          0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x22, 0x12, 0x0E, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x20,
          0x00, 0x3F, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3F, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x3F, 0x00,
          0x00, 0x3F, 0x11, 0x20, 0x20, 0x1F, 0x00, 0x00, 0x20, 0x00, 0x80, 0x7F, 0x00, 0x00, 0xFF, 0x11,
          0x20, 0x20, 0x1F, 0x00, 0x00, 0x3F, 0x00, 0x01, 0x0E, 0x30, 0x0E, 0x01, 0x0E, 0x30, 0x0E, 0x01,
          0x00, 0x1F, 0x20, 0x20, 0x20, 0x1F, 0x00, 0x00, 0x3F, 0x01, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x9F,
          0xA0, 0xA0, 0x91, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          #endif
          #endif
};

#define ssd1306_swap(a, b) { int16_t t = a; a = b; b = t; }
#define adagfxswap(a, b) { int16_t t = a; a = b; b = t; }

// Return the size of the display (per current rotation)
int16_t ssd1306_width(void)
{
        return _width;
}

int16_t ssd1306_height(void)
{
        return _height;
}

void set_rotation(uint8_t x)
{
        rotation = (x & 3);
        switch (rotation) {
        case 0:
        case 2:
                _width  = WIDTH;
                _height = HEIGHT;
                break;
        case 1:
        case 3:
                _width  = HEIGHT;
                _height = WIDTH;
                break;
        }
}

void ssd1306_command(uint8_t c)
{
        uint8_t dta_send[] = {0x00, c};
        i2c_write_dt(&oled_i2c, dta_send, 2);
}

void ssd1306_begin(uint8_t vccstate, bool reset)
{
        _vccstate = vccstate;

        _width    = WIDTH;
        _height   = HEIGHT;
        cursor_y  = cursor_x    = 0;
        textsize  = 1;
        textcolor = textbgcolor = 0xFFFF;
        wrap      = true;
        _cp437    = false;

        _width = WIDTH = SSD1306_LCDWIDTH;
        _height = HEIGHT = SSD1306_LCDHEIGHT;
        rotation  = 0;


        if (reset) {
                k_sleep(K_MSEC(1));
        }

#if defined SSD1306_128_32
        // Init sequence for 128x32 OLED module
        ssd1306_command(SSD1306_DISPLAYOFF);                // 0xAE
        ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV);        // 0xD5
        ssd1306_command(0x80);                              // the suggested ratio 0x80

        ssd1306_command(SSD1306_SETMULTIPLEX);              // 0xA8
        ssd1306_command(0x1F);
        ssd1306_command(SSD1306_SETDISPLAYOFFSET);          // 0xD3
        ssd1306_command(0x0);                               // no offset
        ssd1306_command(SSD1306_SETSTARTLINE | 0x0);        // line #0
        ssd1306_command(SSD1306_CHARGEPUMP);                // 0x8D
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x10);
        }
        else {
                ssd1306_command(0x14);
        }
        ssd1306_command(SSD1306_MEMORYMODE);                // 0x20
        ssd1306_command(0x00);                              // 0x0 act like ks0108
        ssd1306_command(SSD1306_SEGREMAP | 0x1);
        ssd1306_command(SSD1306_COMSCANDEC);
        ssd1306_command(SSD1306_SETCOMPINS);                // 0xDA
        ssd1306_command(0x02);
        ssd1306_command(SSD1306_SETCONTRAST);               // 0x81
        ssd1306_command(0x8F);
        ssd1306_command(SSD1306_SETPRECHARGE);              // 0xd9
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x22);
        }
        else {
                ssd1306_command(0xF1);
        }
        ssd1306_command(SSD1306_SETVCOMDETECT);             // 0xDB
        ssd1306_command(0x40);
        ssd1306_command(SSD1306_DISPLAYALLON_RESUME);       // 0xA4
        ssd1306_command(SSD1306_NORMALDISPLAY);             // 0xA6
#endif

#if defined SSD1306_128_64
        // Init sequence for 128x64 OLED module
        ssd1306_command(SSD1306_DISPLAYOFF);                // 0xAE
        ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV);        // 0xD5
        ssd1306_command(0x80);                              // the suggested ratio 0x80
        ssd1306_command(SSD1306_SETMULTIPLEX);              // 0xA8
        ssd1306_command(0x3F);
        ssd1306_command(SSD1306_SETDISPLAYOFFSET);          // 0xD3
        ssd1306_command(0x0);                               // no offset
        ssd1306_command(SSD1306_SETSTARTLINE | 0x0);        // line #0
        ssd1306_command(SSD1306_CHARGEPUMP);                // 0x8D
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x10);
        }
        else {
                ssd1306_command(0x14);
        }
        ssd1306_command(SSD1306_MEMORYMODE);                // 0x20
        ssd1306_command(0x00);                              // 0x0 act like ks0108
        ssd1306_command(SSD1306_SEGREMAP | 0x1);
        ssd1306_command(SSD1306_COMSCANDEC);
        ssd1306_command(SSD1306_SETCOMPINS);                // 0xDA
        ssd1306_command(0x12);
        ssd1306_command(SSD1306_SETCONTRAST);               // 0x81
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x9F);
        }
        else {
                ssd1306_command(0xCF);
        }
        ssd1306_command(SSD1306_SETPRECHARGE);              // 0xd9
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x22);
        }
        else {
                ssd1306_command(0xF1);
        }
        ssd1306_command(SSD1306_SETVCOMDETECT);             // 0xDB
        ssd1306_command(0x40);
        ssd1306_command(SSD1306_DISPLAYALLON_RESUME);       // 0xA4
        ssd1306_command(SSD1306_NORMALDISPLAY);             // 0xA6
#endif

#if defined SSD1306_96_16
        // Init sequence for 96x16 OLED module
        ssd1306_command(SSD1306_DISPLAYOFF);                // 0xAE
        ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV);        // 0xD5
        ssd1306_command(0x80);                              // the suggested ratio 0x80
        ssd1306_command(SSD1306_SETMULTIPLEX);              // 0xA8
        ssd1306_command(0x0F);
        ssd1306_command(SSD1306_SETDISPLAYOFFSET);          // 0xD3
        ssd1306_command(0x00);                               // no offset
        ssd1306_command(SSD1306_SETSTARTLINE | 0x0);        // line #0
        ssd1306_command(SSD1306_CHARGEPUMP);                // 0x8D
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x10);
        }
        else {
                ssd1306_command(0x14);
        }
        ssd1306_command(SSD1306_MEMORYMODE);                // 0x20
        ssd1306_command(0x00);                              // 0x0 act like ks0108
        ssd1306_command(SSD1306_SEGREMAP | 0x1);
        ssd1306_command(SSD1306_COMSCANDEC);
        ssd1306_command(SSD1306_SETCOMPINS);                // 0xDA
        ssd1306_command(0x2); //ada x12
        ssd1306_command(SSD1306_SETCONTRAST);               // 0x81
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x10);
        }
        else {
                ssd1306_command(0xAF);
        }
        ssd1306_command(SSD1306_SETPRECHARGE);              // 0xd9
        if (vccstate == SSD1306_EXTERNALVCC) {
                ssd1306_command(0x22);
        }
        else {
                ssd1306_command(0xF1);
        }
        ssd1306_command(SSD1306_SETVCOMDETECT);             // 0xDB
        ssd1306_command(0x40);
        ssd1306_command(SSD1306_DISPLAYALLON_RESUME);       // 0xA4
        ssd1306_command(SSD1306_NORMALDISPLAY);             // 0xA6
#endif

        ssd1306_command(SSD1306_DISPLAYON);//--turn on oled panel
}



// the most basic function, set a single pixel
void ssd1306_draw_pixel(int16_t x, int16_t y, uint16_t color)
{
        if ((x < 0) || (x >= ssd1306_width()) || (y < 0) || (y >= ssd1306_height()))
                return;

        // check rotation, move pixel around if necessary
        switch (rotation) {
        case 1:
                ssd1306_swap(x, y);
                x = WIDTH - x - 1;
                break;
        case 2:
                x = WIDTH - x - 1;
                y = HEIGHT - y - 1;
                break;
        case 3:
                ssd1306_swap(x, y);
                y = HEIGHT - y - 1;
                break;
        }

        // x is which column
        switch (color) {
        case WHITE:
                buffer[x + (y / 8)*SSD1306_LCDWIDTH] |=  (1 << (y & 7));
                break;
        case BLACK:
                buffer[x + (y / 8)*SSD1306_LCDWIDTH] &= ~(1 << (y & 7));
                break;
        case INVERSE:
                buffer[x + (y / 8)*SSD1306_LCDWIDTH] ^=  (1 << (y & 7));
                break;
        }
}


void ssd1306_invert_display(uint8_t i)
{
        if (i) {
                ssd1306_command(SSD1306_INVERTDISPLAY);
        }
        else {
                ssd1306_command(SSD1306_NORMALDISPLAY);
        }
}


// startscrollright
// Activate a right handed scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void ssd1306_start_scroll_right(uint8_t start, uint8_t stop)
{
        ssd1306_command(SSD1306_RIGHT_HORIZONTAL_SCROLL);
        ssd1306_command(0X00);
        ssd1306_command(start);
        ssd1306_command(0X00);
        ssd1306_command(stop);
        ssd1306_command(0X00);
        ssd1306_command(0XFF);
        ssd1306_command(SSD1306_ACTIVATE_SCROLL);
}

// startscrollleft
// Activate a right handed scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void ssd1306_start_scroll_left(uint8_t start, uint8_t stop)
{
        ssd1306_command(SSD1306_LEFT_HORIZONTAL_SCROLL);
        ssd1306_command(0X00);
        ssd1306_command(start);
        ssd1306_command(0X00);
        ssd1306_command(stop);
        ssd1306_command(0X00);
        ssd1306_command(0XFF);
        ssd1306_command(SSD1306_ACTIVATE_SCROLL);
}

// startscrolldiagright
// Activate a diagonal scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void ssd1306_start_scroll_diag_right(uint8_t start, uint8_t stop)
{
        ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA);
        ssd1306_command(0X00);
        ssd1306_command(SSD1306_LCDHEIGHT);
        ssd1306_command(SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL);
        ssd1306_command(0X00);
        ssd1306_command(start);
        ssd1306_command(0X00);
        ssd1306_command(stop);
        ssd1306_command(0X01);
        ssd1306_command(SSD1306_ACTIVATE_SCROLL);
}

// startscrolldiagleft
// Activate a diagonal scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void ssd1306_start_scroll_diag_left(uint8_t start, uint8_t stop)
{
        ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA);
        ssd1306_command(0X00);
        ssd1306_command(SSD1306_LCDHEIGHT);
        ssd1306_command(SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL);
        ssd1306_command(0X00);
        ssd1306_command(start);
        ssd1306_command(0X00);
        ssd1306_command(stop);
        ssd1306_command(0X01);
        ssd1306_command(SSD1306_ACTIVATE_SCROLL);
}

void ssd1306_stop_scroll(void)
{
        ssd1306_command(SSD1306_DEACTIVATE_SCROLL);
}

// Dim the display
// dim = true: display is dimmed
// dim = false: display is normal
void ssd1306_dim(bool dim)
{
        uint8_t contrast;

        if (dim) {
                contrast = 0; // Dimmed display
        }
        else {
                if (_vccstate == SSD1306_EXTERNALVCC) {
                        contrast = 0x9F;
                }
                else {
                        contrast = 0xCF;
                }
        }
        // the range of contrast to too small to be really useful
        // it is useful to dim the display
        ssd1306_command(SSD1306_SETCONTRAST);
        ssd1306_command(contrast);
}

void ssd1306_data(uint8_t c)
{
        uint8_t dta_send[] = {0x40, c};
        i2c_write_dt(&oled_i2c, dta_send, 2);
}

void ssd1306_display(void)
{
        ssd1306_command(SSD1306_COLUMNADDR);
        ssd1306_command(0); // Column start address (0 = reset)
        ssd1306_command(SSD1306_LCDWIDTH - 1); // Column end address (127 = reset)

        ssd1306_command(SSD1306_PAGEADDR);
        ssd1306_command(0); // Page start address (0 = reset)
#if SSD1306_LCDHEIGHT == 64
        ssd1306_command(7); // Page end address
#endif
#if SSD1306_LCDHEIGHT == 32
        ssd1306_command(3); // Page end address
#endif
#if SSD1306_LCDHEIGHT == 16
        ssd1306_command(1); // Page end address
#endif

        // I2C
        for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++)
        {
                uint8_t tmpBuf[17];
                // SSD1306_SETSTARTLINE
                tmpBuf[0] = 0x40;
                // data
                for (uint8_t j = 0; j < 16; j++) {
                        tmpBuf[j+1] = buffer[i];
                        i++;
                }
                i--;

                i2c_write_dt(&oled_i2c, tmpBuf, sizeof(tmpBuf));
        }
}

// clear everything
void ssd1306_clear_display(void)
{
        memset(buffer, 0, (SSD1306_LCDWIDTH * SSD1306_LCDHEIGHT / 8));
}

void ssd1306_draw_fast_hline(int16_t x, int16_t y, int16_t w, uint16_t color)
{
        bool __swap = false;
        switch (rotation) {
        case 0:
                // 0 degree rotation, do nothing
                break;
        case 1:
                // 90 degree rotation, swap x & y for rotation, then invert x
                __swap = true;
                ssd1306_swap(x, y);
                x = WIDTH - x - 1;
                break;
        case 2:
                // 180 degree rotation, invert x and y - then shift y around for height.
                x = WIDTH - x - 1;
                y = HEIGHT - y - 1;
                x -= (w - 1);
                break;
        case 3:
                // 270 degree rotation, swap x & y for rotation, then invert y  and adjust y for w (not to become h)
                __swap = true;
                ssd1306_swap(x, y);
                y = HEIGHT - y - 1;
                y -= (w - 1);
                break;
        }

        if (__swap) {
                ssd1306_draw_fast_vline_internal(x, y, w, color);
        }
        else {
                ssd1306_draw_fast_hline_internal(x, y, w, color);
        }
}

void ssd1306_draw_fast_hline_internal(int16_t x, int16_t y, int16_t w, uint16_t color)
{
        // Do bounds/limit checks
        if (y < 0 || y >= HEIGHT) {
                return;
        }

        // make sure we don't try to draw below 0
        if (x < 0) {
                w += x;
                x = 0;
        }

        // make sure we don't go off the edge of the display
        if ( (x + w) > WIDTH) {
                w = (WIDTH - x);
        }

        // if our width is now negative, punt
        if (w <= 0) {
                return;
        }

        // set up the pointer for  movement through the buffer
        register uint8_t *pBuf = buffer;
        // adjust the buffer pointer for the current row
        pBuf += ((y / 8) * SSD1306_LCDWIDTH);
        // and offset x columns in
        pBuf += x;

        register uint8_t mask = 1 << (y & 7);

        switch (color) {
        case WHITE:
                while (w--) {
                        *pBuf++ |= mask;
                };
                break;
        case BLACK:
                mask = ~mask;
                while (w--) {
                        *pBuf++ &= mask;
                };
                break;
        case INVERSE:
                while (w--) {
                        *pBuf++ ^= mask;
                };
                break;
        }
}

void ssd1306_draw_fast_vline(int16_t x, int16_t y, int16_t h, uint16_t color)
{
        bool __swap = false;
        switch (rotation) {
        case 0:
                break;
        case 1:
                // 90 degree rotation, swap x & y for rotation, then invert x and adjust x for h (now to become w)
                __swap = true;
                ssd1306_swap(x, y);
                x = WIDTH - x - 1;
                x -= (h - 1);
                break;
        case 2:
                // 180 degree rotation, invert x and y - then shift y around for height.
                x = WIDTH - x - 1;
                y = HEIGHT - y - 1;
                y -= (h - 1);
                break;
        case 3:
                // 270 degree rotation, swap x & y for rotation, then invert y
                __swap = true;
                ssd1306_swap(x, y);
                y = HEIGHT - y - 1;
                break;
        }

        if (__swap) {
                ssd1306_draw_fast_hline_internal(x, y, h, color);
        }
        else {
                ssd1306_draw_fast_vline_internal(x, y, h, color);
        }
}


void ssd1306_draw_fast_vline_internal(int16_t x, int16_t __y, int16_t __h, uint16_t color)
{

        // do nothing if we're off the left or right side of the screen
        if (x < 0 || x >= WIDTH) {
                return;
        }

        // make sure we don't try to draw below 0
        if (__y < 0) {
                // __y is negative, this will subtract enough from __h to account for __y being 0
                __h += __y;
                __y = 0;

        }

        // make sure we don't go past the height of the display
        if ( (__y + __h) > HEIGHT) {
                __h = (HEIGHT - __y);
        }

        // if our height is now negative, punt
        if (__h <= 0) {
                return;
        }

        // this display doesn't need ints for coordinates, use local byte registers for faster juggling
        register uint8_t y = __y;
        register uint8_t h = __h;


        // set up the pointer for fast movement through the buffer
        register uint8_t *pBuf = buffer;
        // adjust the buffer pointer for the current row
        pBuf += ((y / 8) * SSD1306_LCDWIDTH);
        // and offset x columns in
        pBuf += x;

        // do the first partial byte, if necessary - this requires some masking
        register uint8_t mod = (y & 7);
        if (mod) {
                // mask off the high n bits we want to set
                mod = 8 - mod;

                // note - lookup table results in a nearly 10% performance improvement in fill* functions
                // register uint8_t mask = ~(0xFF >> (mod));
                static uint8_t premask[8] = {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
                register uint8_t mask = premask[mod];

                // adjust the mask if we're not going to reach the end of this byte
                if ( h < mod) {
                        mask &= (0XFF >> (mod - h));
                }

                switch (color) {
                case WHITE:
                        *pBuf |=  mask;
                        break;
                case BLACK:
                        *pBuf &= ~mask;
                        break;
                case INVERSE:
                        *pBuf ^=  mask;
                        break;
                }

                // fast exit if we're done here!
                if (h < mod) {
                        return;
                }

                h -= mod;

                pBuf += SSD1306_LCDWIDTH;
        }


        // write solid bytes while we can - effectively doing 8 rows at a time
        if (h >= 8) {
                if (color == INVERSE)  {  // separate copy of the code so we don't impact performance of the black/white write version with an extra comparison per loop
                        do  {
                                *pBuf = ~(*pBuf);

                                // adjust the buffer forward 8 rows worth of data
                                pBuf += SSD1306_LCDWIDTH;

                                // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now)
                                h -= 8;
                        }
                        while (h >= 8);
                }
                else {
                        // store a local value to work with
                        register uint8_t val = (color == WHITE) ? 255 : 0;

                        do  {
                                // write our value in
                                *pBuf = val;

                                // adjust the buffer forward 8 rows worth of data
                                pBuf += SSD1306_LCDWIDTH;

                                // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now)
                                h -= 8;
                        }
                        while (h >= 8);
                }
        }

        // now do the final partial byte, if necessary
        if (h) {
                mod = h & 7;
                // this time we want to mask the low bits of the byte, vs the high bits we did above
                // register uint8_t mask = (1 << mod) - 1;
                // note - lookup table results in a nearly 10% performance improvement in fill* functions
                static uint8_t postmask[8] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F };
                register uint8_t mask = postmask[mod];
                switch (color) {
                case WHITE:
                        *pBuf |=  mask;
                        break;
                case BLACK:
                        *pBuf &= ~mask;
                        break;
                case INVERSE:
                        *pBuf ^=  mask;
                        break;
                }
        }
}


// Draw a circle outline
void ssd1306_draw_circle(int16_t x0, int16_t y0, int16_t r, uint16_t color)
{
        int16_t f = 1 - r;
        int16_t ddF_x = 1;
        int16_t ddF_y = -2 * r;
        int16_t x = 0;
        int16_t y = r;

        ssd1306_draw_pixel(x0, y0 + r, color);
        ssd1306_draw_pixel(x0, y0 - r, color);
        ssd1306_draw_pixel(x0 + r, y0, color);
        ssd1306_draw_pixel(x0 - r, y0, color);

        while (x < y) {
                if (f >= 0) {
                        y--;
                        ddF_y += 2;
                        f += ddF_y;
                }
                x++;
                ddF_x += 2;
                f += ddF_x;

                ssd1306_draw_pixel(x0 + x, y0 + y, color);
                ssd1306_draw_pixel(x0 - x, y0 + y, color);
                ssd1306_draw_pixel(x0 + x, y0 - y, color);
                ssd1306_draw_pixel(x0 - x, y0 - y, color);
                ssd1306_draw_pixel(x0 + y, y0 + x, color);
                ssd1306_draw_pixel(x0 - y, y0 + x, color);
                ssd1306_draw_pixel(x0 + y, y0 - x, color);
                ssd1306_draw_pixel(x0 - y, y0 - x, color);
        }
}

void ssd1306_draw_circle_helper(int16_t x0, int16_t y0,
                                int16_t r, uint8_t cornername, uint16_t color)
{
        int16_t f     = 1 - r;
        int16_t ddF_x = 1;
        int16_t ddF_y = -2 * r;
        int16_t x     = 0;
        int16_t y     = r;

        while (x < y) {
                if (f >= 0) {
                        y--;
                        ddF_y += 2;
                        f     += ddF_y;
                }
                x++;
                ddF_x += 2;
                f     += ddF_x;
                if (cornername & 0x4) {
                        ssd1306_draw_pixel(x0 + x, y0 + y, color);
                        ssd1306_draw_pixel(x0 + y, y0 + x, color);
                }
                if (cornername & 0x2) {
                        ssd1306_draw_pixel(x0 + x, y0 - y, color);
                        ssd1306_draw_pixel(x0 + y, y0 - x, color);
                }
                if (cornername & 0x8) {
                        ssd1306_draw_pixel(x0 - y, y0 + x, color);
                        ssd1306_draw_pixel(x0 - x, y0 + y, color);
                }
                if (cornername & 0x1) {
                        ssd1306_draw_pixel(x0 - y, y0 - x, color);
                        ssd1306_draw_pixel(x0 - x, y0 - y, color);
                }
        }
}

void ssd1306_fill_circle(int16_t x0, int16_t y0, int16_t r, uint16_t color)
{
        ssd1306_draw_fast_vline(x0, y0 - r, 2 * r + 1, color);
        ssd1306_fill_circle_helper(x0, y0, r, 3, 0, color);
}

// Used to do circles and roundrects
void ssd1306_fill_circle_helper(int16_t x0, int16_t y0, int16_t r,
                                uint8_t cornername, int16_t delta, uint16_t color)
{

        int16_t f     = 1 - r;
        int16_t ddF_x = 1;
        int16_t ddF_y = -2 * r;
        int16_t x     = 0;
        int16_t y     = r;

        while (x < y) {
                if (f >= 0) {
                        y--;
                        ddF_y += 2;
                        f     += ddF_y;
                }
                x++;
                ddF_x += 2;
                f     += ddF_x;

                if (cornername & 0x1) {
                        ssd1306_draw_fast_vline(x0 + x, y0 - y, 2 * y + 1 + delta, color);
                        ssd1306_draw_fast_vline(x0 + y, y0 - x, 2 * x + 1 + delta, color);
                }
                if (cornername & 0x2) {
                        ssd1306_draw_fast_vline(x0 - x, y0 - y, 2 * y + 1 + delta, color);
                        ssd1306_draw_fast_vline(x0 - y, y0 - x, 2 * x + 1 + delta, color);
                }
        }
}

// Bresenham's algorithm - thx wikpedia
void ssd1306_draw_line(int16_t x0, int16_t y0,
                       int16_t x1, int16_t y1,
                       uint16_t color)
{
        int16_t steep = abs(y1 - y0) > abs(x1 - x0);
        if (steep) {
                adagfxswap(x0, y0);
                adagfxswap(x1, y1);
        }

        if (x0 > x1) {
                adagfxswap(x0, x1);
                adagfxswap(y0, y1);
        }

        int16_t dx, dy;
        dx = x1 - x0;
        dy = abs(y1 - y0);

        int16_t err = dx / 2;
        int16_t ystep;

        if (y0 < y1) {
                ystep = 1;
        }
        else {
                ystep = -1;
        }

        for (; x0 <= x1; x0++) {
                if (steep) {
                        ssd1306_draw_pixel(y0, x0, color);
                }
                else {
                        ssd1306_draw_pixel(x0, y0, color);
                }
                err -= dy;
                if (err < 0) {
                        y0 += ystep;
                        err += dx;
                }
        }
}

// Draw a rectangle
void ssd1306_draw_rect(int16_t x, int16_t y,
                       int16_t w, int16_t h,
                       uint16_t color)
{
        ssd1306_draw_fast_hline(x, y, w, color);
        ssd1306_draw_fast_hline(x, y + h - 1, w, color);
        ssd1306_draw_fast_vline(x, y, h, color);
        ssd1306_draw_fast_vline(x + w - 1, y, h, color);
}

#if 0
void Adafruit_GFX::drawFastVLine(int16_t x, int16_t y,
                                 int16_t h, uint16_t color)
{
        // Update in subclasses if desired!
        drawLine(x, y, x, y + h - 1, color);
}

void Adafruit_GFX::drawFastHLine(int16_t x, int16_t y,
                                 int16_t w, uint16_t color)
{
        // Update in subclasses if desired!
        drawLine(x, y, x + w - 1, y, color);
}
#endif

void ssd1306_fill_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
        // Update in subclasses if desired!
        for (int16_t i = x; i < x + w; i++) {
                ssd1306_draw_fast_vline(i, y, h, color);
        }
}

void ssd1306_fill_screen(uint16_t color)
{
        ssd1306_fill_rect(0, 0, _width, _height, color);
}

// Draw a rounded rectangle
void ssd1306_draw_round_rect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color)
{
        // smarter version
        ssd1306_draw_fast_hline(x + r, y, w - 2 * r, color);   // Top
        ssd1306_draw_fast_hline(x + r, y + h - 1, w - 2 * r, color); // Bottom
        ssd1306_draw_fast_vline(x, y + r, h - 2 * r, color);   // Left
        ssd1306_draw_fast_vline(x + w - 1, y + r, h - 2 * r, color); // Right
        // draw four corners
        ssd1306_draw_circle_helper(x + r, y + r, r, 1, color);
        ssd1306_draw_circle_helper(x + w - r - 1, y + r, r, 2, color);
        ssd1306_draw_circle_helper(x + w - r - 1, y + h - r - 1, r, 4, color);
        ssd1306_draw_circle_helper(x + r, y + h - r - 1, r, 8, color);
}

// Fill a rounded rectangle
void ssd1306_fill_round_rect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color)
{
        // smarter version
        ssd1306_fill_rect(x + r, y, w - 2 * r, h, color);

        // draw four corners
        ssd1306_fill_circle_helper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color);
        ssd1306_fill_circle_helper(x + r, y + r, r, 2, h - 2 * r - 1, color);
}

// Draw a triangle
void ssd1306_draw_triangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)
{
        ssd1306_draw_line(x0, y0, x1, y1, color);
        ssd1306_draw_line(x1, y1, x2, y2, color);
        ssd1306_draw_line(x2, y2, x0, y0, color);
}

// Fill a triangle
void ssd1306_fill_triangle( int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)
{
        int16_t a, b, y, last;

        // Sort coordinates by Y order (y2 >= y1 >= y0)
        if (y0 > y1) {
                adagfxswap(y0, y1);
                adagfxswap(x0, x1);
        }
        if (y1 > y2) {
                adagfxswap(y2, y1);
                adagfxswap(x2, x1);
        }
        if (y0 > y1) {
                adagfxswap(y0, y1);
                adagfxswap(x0, x1);
        }

        if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing
                a = b = x0;
                if (x1 < a) a = x1;
                else if (x1 > b) b = x1;
                if (x2 < a) a = x2;
                else if (x2 > b) b = x2;
                ssd1306_draw_fast_hline(a, y0, b - a + 1, color);
                return;
        }

        int16_t
                dx01 = x1 - x0,
                dy01 = y1 - y0,
                dx02 = x2 - x0,
                dy02 = y2 - y0,
                dx12 = x2 - x1,
                dy12 = y2 - y1;
        int32_t
                sa   = 0,
                sb   = 0;

        // For upper part of triangle, find scanline crossings for segments
        // 0-1 and 0-2.  If y1=y2 (flat-bottomed triangle), the scanline y1
        // is included here (and second loop will be skipped, avoiding a /0
        // error there), otherwise scanline y1 is skipped here and handled
...

This file has been truncated, please download it to see its full contents.

ssd1306.h

C/C++
#ifndef __SSD1306_LIB_H
#define __SSD1306_LIB_H

#define pgm_read_byte(addr) (*(const unsigned char *)(addr))


#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

typedef volatile uint8_t PortReg;
typedef uint32_t PortMask;


#define BLACK 0
#define WHITE 1
#define INVERSE 2

#define SSD1306_I2C_ADDRESS   (0x3C)  // 011110+SA0+RW - 0x3C or 0x3D
// Address for 128x32 is 0x3C
// Address for 128x64 is 0x3D (default) or 0x3C (if SA0 is grounded)

/*=========================================================================
    SSD1306 Displays
    -----------------------------------------------------------------------
    The driver is used in multiple displays (128x64, 128x32, etc.).
    Select the appropriate display below to create an appropriately
    sized framebuffer, etc.
    SSD1306_128_64  128x64 pixel display
    SSD1306_128_32  128x32 pixel display
    SSD1306_96_16
    -----------------------------------------------------------------------*/
#define SSD1306_128_64
//   #define SSD1306_128_32
//   #define SSD1306_96_16
/*=========================================================================*/


#if defined SSD1306_128_64
#define SSD1306_LCDWIDTH                  128
#define SSD1306_LCDHEIGHT                 64
#endif
#if defined SSD1306_128_32
#define SSD1306_LCDWIDTH                  128
#define SSD1306_LCDHEIGHT                 32
#endif
#if defined SSD1306_96_16
#define SSD1306_LCDWIDTH                  96
#define SSD1306_LCDHEIGHT                 16
#endif

#define SSD1306_SETCONTRAST 0x81
#define SSD1306_DISPLAYALLON_RESUME 0xA4
#define SSD1306_DISPLAYALLON 0xA5
#define SSD1306_NORMALDISPLAY 0xA6
#define SSD1306_INVERTDISPLAY 0xA7
#define SSD1306_DISPLAYOFF 0xAE
#define SSD1306_DISPLAYON 0xAF

#define SSD1306_SETDISPLAYOFFSET 0xD3
#define SSD1306_SETCOMPINS 0xDA

#define SSD1306_SETVCOMDETECT 0xDB

#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
#define SSD1306_SETPRECHARGE 0xD9

#define SSD1306_SETMULTIPLEX 0xA8

#define SSD1306_SETLOWCOLUMN 0x00
#define SSD1306_SETHIGHCOLUMN 0x10

#define SSD1306_SETSTARTLINE 0x40

#define SSD1306_MEMORYMODE 0x20
#define SSD1306_COLUMNADDR 0x21
#define SSD1306_PAGEADDR   0x22

#define SSD1306_COMSCANINC 0xC0
#define SSD1306_COMSCANDEC 0xC8

#define SSD1306_SEGREMAP 0xA0

#define SSD1306_CHARGEPUMP 0x8D

#define SSD1306_EXTERNALVCC 0x1
#define SSD1306_SWITCHCAPVCC 0x2

// Scrolling #defines
#define SSD1306_ACTIVATE_SCROLL 0x2F
#define SSD1306_DEACTIVATE_SCROLL 0x2E
#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3
#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26
#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A

#ifdef __cplusplus
extern "C" {
#endif

int16_t ssd1306_width(void);
int16_t ssd1306_height(void);
void set_rotation(uint8_t x);
void ssd1306_command(uint8_t c);
void ssd1306_begin(uint8_t vccstate, bool reset);
void ssd1306_draw_pixel(int16_t x, int16_t y, uint16_t color);
void ssd1306_invert_display(uint8_t i);
void ssd1306_start_scroll_right(uint8_t start, uint8_t stop);
void ssd1306_start_scroll_left(uint8_t start, uint8_t stop);
void ssd1306_start_scroll_diag_right(uint8_t start, uint8_t stop);
void ssd1306_start_scroll_diag_left(uint8_t start, uint8_t stop);
void ssd1306_stop_scroll(void);
void ssd1306_dim(bool dim);
void ssd1306_data(uint8_t c);
void ssd1306_display(void);
void ssd1306_clear_display(void);
void ssd1306_draw_fast_hline(int16_t x, int16_t y, int16_t w, uint16_t color);
void ssd1306_draw_fast_hline_internal(int16_t x, int16_t y, int16_t w, uint16_t color);
void ssd1306_draw_fast_vline(int16_t x, int16_t y, int16_t h, uint16_t color);
void ssd1306_draw_fast_vline_internal(int16_t x, int16_t __y, int16_t __h, uint16_t color);
void ssd1306_draw_circle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void ssd1306_draw_circle_helper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color);
void ssd1306_fill_circle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void ssd1306_fill_circle_helper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color);
void ssd1306_draw_line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
void ssd1306_draw_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void ssd1306_fill_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void ssd1306_fill_screen(uint16_t color);
void ssd1306_draw_round_rect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color);
void ssd1306_fill_round_rect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color);
void ssd1306_draw_triangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
void ssd1306_fill_triangle( int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
void ssd1306_draw_bitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color);
void ssd1306_draw_bitmap_bg(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg);
void ssd1306_draw_xbitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color);
size_t ssd1306_write(uint8_t c);
void ssd1306_draw_char(int16_t x, int16_t y, uint8_t c, uint16_t color, uint16_t bg, uint8_t size);
void ssd1306_set_cursor(int16_t x, int16_t y);
int16_t ssd1306_get_cursor_x(void);
int16_t ssd1306_get_cursor_y(void);
void ssd1306_set_textsize(uint8_t s);
void ssd1306_set_textcolor(uint16_t c);
void ssd1306_set_textcolor_bg(uint16_t c, uint16_t b);
void ssd1306_set_textwrap(bool w);
uint8_t ssd1306_get_rotation(void);
void ssd1306_set_rotation(uint8_t x);
void ssd1306_cp437(bool x);
void ssd1306_putstring(char* buffer);
void ssd1306_puts(char* buffer);
#ifdef __cplusplus
}
#endif

#endif /* __SSD1306_LIB_H */

CMakeLists.txt

C/C++
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(hello_world)

#target_sources(app PRIVATE src/main.c)

FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

main.h

C/C++
/*
 * Copyright (c) 2012-2014 Wind River Systems, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/zephyr.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>

#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/adc.h>

#include <stdio.h>
#include <zephyr/sys/__assert.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>


static K_SEM_DEFINE(sem, 0, 1);

#define LED0_NODE DT_ALIAS(led0)
#define LED1_NODE DT_ALIAS(led1)
#define LED2_NODE DT_ALIAS(led2)

#define SW0_NODE	DT_ALIAS(sw0)

static const struct gpio_dt_spec led_red = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
static const struct gpio_dt_spec led_green = GPIO_DT_SPEC_GET(LED1_NODE, gpios);
static const struct gpio_dt_spec led_blue = GPIO_DT_SPEC_GET(LED2_NODE, gpios);

static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios,{0});
//static struct gpio_callback button_cb_data;

#define HAL_delay(delay_ms)	k_sleep(K_MSEC(delay_ms))



#define BH_NODE DT_NODELABEL(bh1749)
static const struct i2c_dt_spec bh_i2c = I2C_DT_SPEC_GET(BH_NODE);

#define ADS_NODE DT_NODELABEL(ads1115)
static const struct i2c_dt_spec ads_i2c = I2C_DT_SPEC_GET(ADS_NODE);

#define PCA_NODE DT_NODELABEL(pca9685)
static const struct i2c_dt_spec pca_i2c = I2C_DT_SPEC_GET(PCA_NODE);

#define OLED_NODE DT_NODELABEL(oled_display)
static const struct i2c_dt_spec oled_i2c = I2C_DT_SPEC_GET(OLED_NODE);

#define NEOPIXEL_NODE DT_NODELABEL(neopixel)
static const struct gpio_dt_spec led_neopixel = GPIO_DT_SPEC_GET(NEOPIXEL_NODE, gpios);

ads1115.c

C/C++
/*!
 * @file ads1115.c
 *
 * twitter: @ArduinoEasy
 */

#include "ads1115.h"
#include "main.h"

uint8_t ads1115_txBuff[3];
uint8_t ads1115_rxBuff[2];

static void ADS1115_WriteRegister(ADS1115 *ads1115_module, uint8_t reg, uint16_t value);
static int16_t ADS1115_ReadRegister(ADS1115 *ads1115_module, uint8_t reg);
static float get_lsb_multiplier(adsGain_t gain);
//static uint32_t get_delay_usec(adsSPS_t sps);
static uint32_t get_delay_msec(adsSPS_t sps);
static uint16_t get_adc_config(ADS1115 *ads1115_module);

void ADS1115_WriteRegister (ADS1115 *ads1115_module, uint8_t reg, uint16_t value){
//	uint8_t data[3];
	ads1115_txBuff[0] = reg;
	ads1115_txBuff[1] = (value >> 8);
	ads1115_txBuff[2] = (value & 0xFF);

  i2c_write_dt(&ads_i2c,ads1115_txBuff,3);
}

int16_t ADS1115_ReadRegister(ADS1115 *ads1115_module, uint8_t reg){
//	uint8_t data[2];
	ads1115_txBuff[0] = reg;

  i2c_write_dt(&ads_i2c,ads1115_txBuff,1);
  i2c_read_dt(&ads_i2c,ads1115_rxBuff,2);

	return (ads1115_rxBuff[0] << 8) | ads1115_rxBuff[1];
}

float get_lsb_multiplier(adsGain_t gain){
	float lsb_multiplier=0;
  switch (gain) {
  case (GAIN_TWOTHIRDS):
    lsb_multiplier = 0.1875;
    break;
  case (GAIN_ONE):
    lsb_multiplier = 0.125;
    break;
  case (GAIN_TWO):
    lsb_multiplier = 0.0625;
    break;
  case (GAIN_FOUR):
    lsb_multiplier = 0.03125;
    break;
  case (GAIN_EIGHT):
    lsb_multiplier = 0.015625;
    break;
  case (GAIN_SIXTEEN):
    lsb_multiplier = 0.0078125;
    break;
	default:
    lsb_multiplier = 0.0625;
    break;
  }
	return lsb_multiplier;
}

/*
uint32_t get_delay_usec(adsSPS_t sps){
	uint32_t delay = 0;
  switch (sps) {
  case (SPS_8):
    delay = 125000;
    break;
  case (SPS_16):
    delay = 62500;
    break;
  case (SPS_32):
    delay = 31250;
    break;
  case (SPS_64):
    delay = 15625;
    break;
  case (SPS_128):
    delay = 7825;
    break;
  case (SPS_250):
    delay = 4000;
    break;
  case (SPS_475):
    delay = 2150;
    break;
  case (SPS_860):
    delay = 1200;
    break;
	default:
    delay = 7825;
    break;
  }
	return delay;
}
*/
uint32_t get_delay_msec(adsSPS_t sps){
	uint32_t delay = 0;
  switch (sps) {
  case (SPS_8):
    delay = 125;
    break;
  case (SPS_16):
    delay = 63;
    break;
  case (SPS_32):
    delay = 32;
    break;
  case (SPS_64):
    delay = 16;
    break;
  case (SPS_128):
    delay = 8;
    break;
  case (SPS_250):
    delay = 4;
    break;
  case (SPS_475):
    delay = 3;
    break;
  case (SPS_860):
    delay = 2;
    break;
	default:
    delay = 8;
    break;
  }
	return delay;
}

uint16_t get_adc_config(ADS1115 *ads1115_module){
	ads1115_module->m_conversionDelay = get_delay_msec(ads1115_module->m_samplingRate);
	ads1115_module->m_lsbMultiplier = get_lsb_multiplier(ads1115_module->m_gain)/1000;

  return
      ADS1115_REG_CONFIG_CQUE_NONE |    // Disable the comparator (default val)
      ADS1115_REG_CONFIG_CLAT_NONLAT |  // Non-latching (default val)
      ADS1115_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
      ads1115_module->m_compMode |							// Comparator Mode
      ads1115_module->m_samplingRate |					// Sampling Rate
      ads1115_module->m_gain |									// PGA Gain
      ads1115_module->m_mode;									// ADC mode
}

/**************************************************************************/
/*!
    @brief  Instantiates a new ADS1115 struct w/appropriate properties
    @param  Device struct
*/
/**************************************************************************/
void ADS1115_init(ADS1115 *ads1115_module){
	// Start with default values
	ads1115_module->m_samplingRate = SPS_128;							// 128 samples per second (default)
	ads1115_module->m_mode 		= ADS1115_REG_CONFIG_MODE_SINGLE;	// Single-shot mode (default)
	ads1115_module->m_gain 		= GAIN_TWOTHIRDS;					// +/- 6.144V range (limited to VDD +0.3V max!)
	ads1115_module->m_compMode 	= CMODE_TRAD;						// Traditional comparator (default val)

  ads1115_module->config =
      ADS1115_REG_CONFIG_CQUE_NONE |    // Disable the comparator (default val)
      ADS1115_REG_CONFIG_CLAT_NONLAT |  // Non-latching (default val)
      ADS1115_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
      ads1115_module->m_compMode |				// Comparator Mode
      ads1115_module->m_samplingRate |			// Sampling Rate
      ads1115_module->m_gain |					// PGA Gain
      ads1115_module->m_mode;					// ADC mode

	ads1115_module->m_conversionDelay = (uint8_t)ADS1115_CONVERSIONDELAY;
}

/**************************************************************************/
/*!
    @brief  Reset a ADS1115
    @param  Device struct
*/
/**************************************************************************/
void ADS1115_reset(ADS1115 *ads1115_module){
//	uint8_t cmd = 0x06;
	ads1115_txBuff[0] = 0x06;

i2c_write_dt(&ads_i2c,ads1115_txBuff,1);
}

/**************************************************************************/
/*!
    @brief  Sets the gain and input voltage range
    @param gain gain setting to use
*/
/**************************************************************************/
void ADS1115_setGain(ADS1115 *ads1115_module, adsGain_t gain){
	ads1115_module->m_gain = gain;
}

/**************************************************************************/
/*!
    @brief  Gets a gain and input voltage range
    @return the gain setting
*/
/**************************************************************************/
adsGain_t ADS1115_getGain(ADS1115 *ads1115_module){
	return ads1115_module->m_gain;
}

/**************************************************************************/
/*!
    @brief  Sets the Sampling rate
		@param  sps: sampling rate to use
*/
/**************************************************************************/
void ADS1115_setSPS(ADS1115 *ads1115_module, adsSPS_t sps){
	ads1115_module->m_samplingRate = sps;
}

/**************************************************************************/
/*!
    @brief  Gets the Sampling rate
    @return the sampling rate
*/
/**************************************************************************/
adsSPS_t ADS1115_getSPS(ADS1115 *ads1115_module){
	return ads1115_module->m_samplingRate;
}

/**************************************************************************/
/*!
    @brief  Sets a ADC conversion mode setting
    @param  mode
*/
/**************************************************************************/
void ADS1115_setCONV(ADS1115 *ads1115_module, adsCONV_t mode){
	ads1115_module->m_mode = mode;
}

/**************************************************************************/
/*!
    @brief  Gets a ADC conversion mode setting
    @return the conversion setting
*/
/**************************************************************************/
adsCONV_t ADS1115_getCONV(ADS1115 *ads1115_module){
	return ads1115_module->m_mode;
}

/**************************************************************************/
/*!
    @brief  Gets a single-ended ADC reading from the specified channel
    @param channel ADC channel to read
    @return the ADC reading
*/
/**************************************************************************/
float ADS1115_readADC(ADS1115 *ads1115_module, adc_Ch_t channel){
  if ((channel & ~ADS1115_REG_CONFIG_MUX_MASK) != 0) {
    return 0;
  }

  uint16_t config = get_adc_config(ads1115_module);;
	config |= channel;
  config |= ADS1115_REG_CONFIG_OS_SINGLE;

	ads1115_module->config = config;

	uint16_t ret_val;
	do{
		ret_val = ADS1115_ReadRegister(ads1115_module, ADS1115_REG_POINTER_CONFIG);
	}
	while( (ret_val & ADS1115_REG_CONFIG_OS_MASK) == ADS1115_REG_CONFIG_OS_BUSY);

	do{
		ADS1115_WriteRegister(ads1115_module, ADS1115_REG_POINTER_CONFIG, config);
		ret_val = ADS1115_ReadRegister(ads1115_module, ADS1115_REG_POINTER_CONFIG);
	}while( (ret_val & ADS1115_REG_CONFIG_MUX_MASK) != (config & ADS1115_REG_CONFIG_MUX_MASK) );

	HAL_delay(ads1115_module->m_conversionDelay);

	do{
		ret_val = ADS1115_ReadRegister(ads1115_module, ADS1115_REG_POINTER_CONFIG);
	}
	while( (ret_val & ADS1115_REG_CONFIG_OS_MASK) == ADS1115_REG_CONFIG_OS_BUSY);

	if(channel>ADS1115_REG_CONFIG_MUX_DIFF_2_3)
		return (float)abs(ADS1115_ReadRegister(ads1115_module, ADS1115_REG_POINTER_CONVERT)) * ads1115_module->m_lsbMultiplier;
	else
		return (float)ADS1115_ReadRegister(ads1115_module, ADS1115_REG_POINTER_CONVERT) * ads1115_module->m_lsbMultiplier;
}

/**************************************************************************/
/*!
    @brief  Gets a single-ended ADC reading from the specified channel
    @param channel ADC channel to read
    @return the ADC reading
*/
/**************************************************************************/
int16_t ADS1115_readADC_raw(ADS1115 *ads1115_module, adc_Ch_t channel){

  if ((channel & ~ADS1115_REG_CONFIG_MUX_MASK) != 0) {
    return 0;
  }

  uint16_t config = get_adc_config(ads1115_module);;
	config |= channel;
  config |= ADS1115_REG_CONFIG_OS_SINGLE;

	ads1115_module->config = config;

	uint16_t ret_val = ADS1115_REG_CONFIG_OS_BUSY;
	do{
		ret_val = ADS1115_ReadRegister(ads1115_module, ADS1115_REG_POINTER_CONFIG);
	}
	while( (ret_val & ADS1115_REG_CONFIG_OS_MASK) == ADS1115_REG_CONFIG_OS_BUSY);

	do{
		ADS1115_WriteRegister(ads1115_module, ADS1115_REG_POINTER_CONFIG, config);
		ret_val = ADS1115_ReadRegister(ads1115_module, ADS1115_REG_POINTER_CONFIG);
	}while( (ret_val & ADS1115_REG_CONFIG_MUX_MASK) != (config & ADS1115_REG_CONFIG_MUX_MASK) );

  HAL_delay(ads1115_module->m_conversionDelay);

	do{
		ret_val = ADS1115_ReadRegister(ads1115_module, ADS1115_REG_POINTER_CONFIG);
	}
	while( (ret_val & ADS1115_REG_CONFIG_OS_MASK) == ADS1115_REG_CONFIG_OS_BUSY);

	return ADS1115_ReadRegister(ads1115_module, ADS1115_REG_POINTER_CONVERT);
}

/**************************************************************************/
/*!
    @brief  Sets up the comparator to operate in basic mode, causing the
            ALERT/RDY pin to assert (go from high to low) when the ADC
            value exceeds the specified threshold.
            This will also set the ADC in continuous conversion mode.
    @param channel ADC channel to use
    @param threshold comparator threshold
*/
/**************************************************************************/
void ADS1115_startComparator_SingleEnded(ADS1115 *ads1115_module, uint8_t channel, int16_t threshold){
	// Start with default values
  uint16_t config =
      ADS1115_REG_CONFIG_CQUE_1CONV |   // Comparator enabled and asserts on 1 match
      ADS1115_REG_CONFIG_CLAT_LATCH |   // Latching mode
      ADS1115_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
      ads1115_module->m_compMode |							// Comparator Mode
      ads1115_module->m_samplingRate |					// Sampling Rate
			ads1115_module->m_gain |									// ADC gain setting
      ADS1115_REG_CONFIG_MODE_CONTIN;   // Continuous conversion mode

  // Set single-ended input channel
  switch (channel) {
  case (0):
    config |= ADS1115_REG_CONFIG_MUX_SINGLE_0;
    break;
  case (1):
    config |= ADS1115_REG_CONFIG_MUX_SINGLE_1;
    break;
  case (2):
    config |= ADS1115_REG_CONFIG_MUX_SINGLE_2;
    break;
  case (3):
    config |= ADS1115_REG_CONFIG_MUX_SINGLE_3;
    break;
  }

	ads1115_module->config = config;

  // Set the high threshold register
	ADS1115_WriteRegister(ads1115_module, ADS1115_REG_POINTER_HITHRESH, threshold);

  // Write config register to the ADC
  ADS1115_WriteRegister(ads1115_module, ADS1115_REG_POINTER_CONFIG, config);
}

/**************************************************************************/
/*!
    @brief  In order to clear the comparator, we need to read the
            conversion results.  This function reads the last conversion
            results without changing the config value.
    @return the last ADC reading
*/
/**************************************************************************/
float ADS1115_getLastConversionResults(ADS1115 *ads1115_module){
	uint16_t ret_val = ADS1115_REG_CONFIG_OS_BUSY;
  // Wait for the conversion to complete
  HAL_delay(ads1115_module->m_conversionDelay);
	do{
		ret_val = ADS1115_ReadRegister(ads1115_module, ADS1115_REG_POINTER_CONFIG);
	}
	while( (ret_val & ADS1115_REG_CONFIG_OS_MASK) == ADS1115_REG_CONFIG_OS_BUSY);

  // Read the conversion results
	return (float)ADS1115_ReadRegister(ads1115_module, ADS1115_REG_POINTER_CONVERT) * ads1115_module->m_lsbMultiplier;
}

/************************************************************************/

ads1115.h

C/C++
/*
 * ads1115.h
 * twitter: @ArduinoEasy
 *
 */

#ifndef ADS1115_H_
#define ADS1115_H_

#include <stdbool.h>
#include <stdint.h>

/*=========================================================================
    I2C ADDRESS/BITS
    -----------------------------------------------------------------------*/
#define ADS1115_ADDRESS (0x48) ///< 100 1000 (ADDR = GND)
/*=========================================================================*/

/*=========================================================================
    CONVERSION DELAY (in mS)
    -----------------------------------------------------------------------*/
//#define ADS1115_CONVERSIONDELAY (1165) ///< Minimum Conversion Delay: usec ( Maximum Sample Rate: 860 SPS )
#define ADS1115_CONVERSIONDELAY (2) ///< Minimum Conversion Delay: msec ( Maximum Sample Rate: 860 SPS )
/*=========================================================================*/

/*=========================================================================
    POINTER REGISTER
    -----------------------------------------------------------------------*/
#define ADS1115_REG_POINTER_MASK		  (0x03)	///< Point mask
#define ADS1115_REG_POINTER_CONVERT		(0x00)	///< Conversion
#define ADS1115_REG_POINTER_CONFIG		(0x01)	///< Configuration
#define ADS1115_REG_POINTER_LOWTHRESH	(0x02)	///< Low threshold
#define ADS1115_REG_POINTER_HITHRESH	(0x03)	///< High threshold
/*=========================================================================*/

/*=========================================================================
    CONFIG REGISTER
    -----------------------------------------------------------------------*/
#define ADS1115_REG_CONFIG_OS_MASK		(0x8000) ///< OS Mask
#define ADS1115_REG_CONFIG_OS_SINGLE	(0x8000) ///< Write: Set to start a single-conversion
#define ADS1115_REG_CONFIG_OS_BUSY		(0x0000) ///< Read: Bit = 0 when conversion is in progress
#define ADS1115_REG_CONFIG_OS_NOTBUSY	(0x8000) ///< Read: Bit = 1 when device is not performing a conversion

#define ADS1115_REG_CONFIG_MUX_MASK 	  (0x7000) ///< Mux Mask
#define ADS1115_REG_CONFIG_MUX_DIFF_0_1	(0x0000) ///< Differential P = AIN0, N = AIN1 (default)
#define ADS1115_REG_CONFIG_MUX_DIFF_0_3	(0x1000) ///< Differential P = AIN0, N = AIN3
#define ADS1115_REG_CONFIG_MUX_DIFF_1_3	(0x2000) ///< Differential P = AIN1, N = AIN3
#define ADS1115_REG_CONFIG_MUX_DIFF_2_3	(0x3000) ///< Differential P = AIN2, N = AIN3

#define ADS1115_REG_CONFIG_MUX_SINGLE_0 (0x4000) ///< Single-ended AIN0
#define ADS1115_REG_CONFIG_MUX_SINGLE_1 (0x5000) ///< Single-ended AIN1
#define ADS1115_REG_CONFIG_MUX_SINGLE_2 (0x6000) ///< Single-ended AIN2
#define ADS1115_REG_CONFIG_MUX_SINGLE_3 (0x7000) ///< Single-ended AIN3


// The ADC input range (or gain) can be changed via the following
// functions, but be careful never to exceed VDD +0.3V max, or to
// exceed the upper and lower limits if you adjust the input range!
// Setting these values incorrectly may destroy your ADC!
//                                                                							ADS1115
//                                                                							-------
// ADS1115_setGain(&adc1115, GAIN_TWOTHIRDS);  // 2/3x gain +/- 6.144V  1 bit = 0.1875mV
// ADS1115_setGain(&adc1115, GAIN_ONE);        // 1x gain   +/- 4.096V  1 bit = 0.125mV
// ADS1115_setGain(&adc1115, GAIN_TWO);        // 2x gain   +/- 2.048V  1 bit = 0.0625mV	(default)
// ADS1115_setGain(&adc1115, GAIN_FOUR);       // 4x gain   +/- 1.024V  1 bit = 0.03125mV
// ADS1115_setGain(&adc1115, GAIN_EIGHT);      // 8x gain   +/- 0.512V  1 bit = 0.015625mV
// ADS1115_setGain(&adc1115, GAIN_SIXTEEN);    // 16x gain  +/- 0.256V  1 bit = 0.0078125mV

#define ADS1115_REG_CONFIG_PGA_MASK		  (0x0E00) ///< PGA Mask
#define ADS1115_REG_CONFIG_PGA_6_144V 	(0x0000) ///< +/-6.144V range = Gain 2/3
#define ADS1115_REG_CONFIG_PGA_4_096V 	(0x0200) ///< +/-4.096V range = Gain 1
#define ADS1115_REG_CONFIG_PGA_2_048V	  (0x0400) ///< +/-2.048V range = Gain 2 (default)
#define ADS1115_REG_CONFIG_PGA_1_024V 	(0x0600) ///< +/-1.024V range = Gain 4
#define ADS1115_REG_CONFIG_PGA_0_512V 	(0x0800) ///< +/-0.512V range = Gain 8
#define ADS1115_REG_CONFIG_PGA_0_256V 	(0x0A00) ///< +/-0.256V range = Gain 16

#define ADS1115_REG_CONFIG_MODE_MASK	  (0x0100)   ///< Mode Mask
#define ADS1115_REG_CONFIG_MODE_CONTIN	(0x0000) ///< Continuous conversion mode
#define ADS1115_REG_CONFIG_MODE_SINGLE	(0x0100) ///< Power-down single-shot mode (default)

#define ADS1115_REG_CONFIG_DR_MASK		(0x00E0)	///< Data Rate Mask
#define ADS1115_REG_CONFIG_DR_8SPS		(0x0000)	///< 8 samples per second
#define ADS1115_REG_CONFIG_DR_16SPS		(0x0020)	///< 16 samples per second
#define ADS1115_REG_CONFIG_DR_32SPS		(0x0040)	///< 32 samples per second
#define ADS1115_REG_CONFIG_DR_64SPS		(0x0060)	///< 64 samples per second
#define ADS1115_REG_CONFIG_DR_128SPS	(0x0080)	///< 128 samples per second (default)
#define ADS1115_REG_CONFIG_DR_250SPS	(0x00A0)	///< 250 samples per second
#define ADS1115_REG_CONFIG_DR_475SPS	(0x00C0)	///< 475 samples per second
#define ADS1115_REG_CONFIG_DR_860SPS	(0x00E0)	///< 860 samples per second

#define ADS1115_REG_CONFIG_CMODE_MASK 	(0x0010) ///< CMode Mask
#define ADS1115_REG_CONFIG_CMODE_TRAD 	(0x0000) ///< Traditional comparator with hysteresis (default)
#define ADS1115_REG_CONFIG_CMODE_WINDOW (0x0010) ///< Window comparator

#define ADS1115_REG_CONFIG_CPOL_MASK	  (0x0008) ///< CPol Mask
#define ADS1115_REG_CONFIG_CPOL_ACTVLOW	(0x0000) ///< ALERT/RDY pin is low when active (default)
#define ADS1115_REG_CONFIG_CPOL_ACTVHI	(0x0008) ///< ALERT/RDY pin is high when active

#define ADS1115_REG_CONFIG_CLAT_MASK	  (0x0004) ///< Determines if ALERT/RDY pin latches once asserted
#define ADS1115_REG_CONFIG_CLAT_NONLAT	(0x0000) ///< Non-latching comparator (default)
#define ADS1115_REG_CONFIG_CLAT_LATCH	  (0x0004) ///< Latching comparator

#define ADS1115_REG_CONFIG_CQUE_MASK	(0x0003) ///< CQue Mask
#define ADS1115_REG_CONFIG_CQUE_1CONV	(0x0000) ///< Assert ALERT/RDY after one conversions
#define ADS1115_REG_CONFIG_CQUE_2CONV	(0x0001) ///< Assert ALERT/RDY after two conversions
#define ADS1115_REG_CONFIG_CQUE_4CONV	(0x0002) ///< Assert ALERT/RDY after four conversions
#define ADS1115_REG_CONFIG_CQUE_NONE	(0x0003) ///< Disable the comparator and put ALERT/RDY in high state (default)
/*=========================================================================*/

/** Gain settings */
typedef enum {
  GAIN_TWOTHIRDS = ADS1115_REG_CONFIG_PGA_6_144V,
  GAIN_ONE 			 = ADS1115_REG_CONFIG_PGA_4_096V,
  GAIN_TWO 			 = ADS1115_REG_CONFIG_PGA_2_048V,
  GAIN_FOUR			 = ADS1115_REG_CONFIG_PGA_1_024V,
  GAIN_EIGHT		 = ADS1115_REG_CONFIG_PGA_0_512V,
  GAIN_SIXTEEN	 = ADS1115_REG_CONFIG_PGA_0_256V
} adsGain_t;

/** Sampling settings */
typedef enum {
  SPS_8 	= ADS1115_REG_CONFIG_DR_8SPS,
  SPS_16 	= ADS1115_REG_CONFIG_DR_16SPS,
  SPS_32	= ADS1115_REG_CONFIG_DR_32SPS,
  SPS_64	= ADS1115_REG_CONFIG_DR_64SPS,
  SPS_128	= ADS1115_REG_CONFIG_DR_128SPS,
  SPS_250	= ADS1115_REG_CONFIG_DR_250SPS,
  SPS_475	= ADS1115_REG_CONFIG_DR_475SPS,
  SPS_860 = ADS1115_REG_CONFIG_DR_860SPS
} adsSPS_t;

/** Comparator Mode */
typedef enum {
  CMODE_TRAD 	= ADS1115_REG_CONFIG_CMODE_TRAD,
  CMODE_WINDOW 	= ADS1115_REG_CONFIG_CMODE_WINDOW
} adsCMODE_t;

/** Conversion Mode */
typedef enum {
  SINGLE_CONV	= ADS1115_REG_CONFIG_MODE_SINGLE,
  CONT_CONV		= ADS1115_REG_CONFIG_MODE_CONTIN
} adsCONV_t;

/** ADC channels */
typedef enum {
	CH_0	= ADS1115_REG_CONFIG_MUX_SINGLE_0,
	CH_1	= ADS1115_REG_CONFIG_MUX_SINGLE_1,
	CH_2	= ADS1115_REG_CONFIG_MUX_SINGLE_2,
	CH_3	= ADS1115_REG_CONFIG_MUX_SINGLE_3,

  DIFF_0_1 = ADS1115_REG_CONFIG_MUX_DIFF_0_1,
  DIFF_0_3 = ADS1115_REG_CONFIG_MUX_DIFF_0_3,
  DIFF_1_3 = ADS1115_REG_CONFIG_MUX_DIFF_1_3,
  DIFF_2_3 = ADS1115_REG_CONFIG_MUX_DIFF_2_3
} adc_Ch_t;

/**************************************************************************/
/*!
    @brief  Sensor driver for the Adafruit ADS1115 ADC breakout.
*/
/**************************************************************************/

/*	Structure to store address and settings of ADS1115 16-bit ADC IC	*/
typedef struct
{
	// Instance-specific properties
	uint16_t		config;					    //< ADC config
	uint16_t		m_samplingRate; 		//< sampling rate
	uint16_t		m_mode; 				    //< ADC mode
	uint32_t		m_conversionDelay;	//< conversion deay
	adsGain_t		m_gain;					    //< ADC gain
	uint16_t		m_compMode;				  //< Comparator Mode
	float			  m_lsbMultiplier;		//< LSB multiplier
	uint16_t		Hi_thresh;				  //< High Threshold value
	uint16_t		Lo_thresh;				  //< Low Threshold value
}ADS1115;


void ADS1115_reset(ADS1115 *ads1115_module);

void ADS1115_init(ADS1115 *ads1115_module);

float ADS1115_readADC(ADS1115 *ads1115_module, adc_Ch_t channel);
int16_t ADS1115_readADC_raw(ADS1115 *ads1115_module, adc_Ch_t channel);

void ADS1115_startComparator_SingleEnded(ADS1115 *ads1115_module, uint8_t channel, int16_t threshold);

float ADS1115_getLastConversionResults(ADS1115 *ads1115_module);

void ADS1115_setGain(ADS1115 *ads1115_module, adsGain_t gain);
adsGain_t ADS1115_getGain(ADS1115 *ads1115_module);

void ADS1115_setSPS(ADS1115 *ads1115_module, adsSPS_t sps);
adsSPS_t ADS1115_getSPS(ADS1115 *ads1115_module);

void ADS1115_setCONV(ADS1115 *ads1115_module, adsCONV_t sps);
adsCONV_t ADS1115_getCONV(ADS1115 *ads1115_module);

#endif /* ADS1115_H_ */

bh1749.c

C/C++
/*!
 * @file bh1749.c
 *
 * twitter: @ArduinoEasy
 */

#include "bh1749.h"
#include "main.h"

uint8_t init_bh1749(void)
{
	int ret;

	if (!device_is_ready(bh_i2c.bus)) {
		printk("I2C bus %s is not ready!\n\r",bh_i2c.bus->name);
		return 0;
	}
	printk("device is %p, name is %s\n", bh_i2c.bus, bh_i2c.bus->name); 

	char buff1[] = {BH1749_MODE_CONTROL1,BH1749_MODE_CONTROL1_DEFAULTS};
	ret = i2c_write_dt(&bh_i2c,buff1,sizeof(buff1));
	if(ret != 0){
		printk("1- Failed to write to I2C device address 0x%c at Reg. 0x%c\n",bh_i2c.addr,BH1749_MODE_CONTROL1);
	}
	
	char buff2[] = {BH1749_MODE_CONTROL2,BH1749_MODE_CONTROL2_RGB_EN_ENABLE};
	ret = i2c_write_dt(&bh_i2c,buff2,sizeof(buff2));
	if(ret != 0){
		printk("2- Failed to write to I2C device address 0x%c at Reg. 0x%c\n",bh_i2c.addr,BH1749_MODE_CONTROL2);
	}
	return 1;
}

void bh1479_read_values(BH_VALUES *bl_val)
{
	int ret;
	uint8_t bh_value[10]= {0};
	//Do a burst read of 6 bytes as each color channel is 2 bytes
	ret = i2c_burst_read_dt(&bh_i2c, BH1749_RED_DATA_LSB,bh_value,sizeof(bh_value));
	if(ret != 0){
	   printk("3-Failed to read to I2C device address 0x%c at Reg. 0x%c\n",bh_i2c.addr,BH1749_RED_DATA_LSB);
	}

	bl_val->bh_red	=bh_value[0] | bh_value[1] << 8;
	bl_val->bh_green=bh_value[2] | bh_value[3] << 8;
	bl_val->bh_blue	=bh_value[4] | bh_value[5] << 8;
	bl_val->bh_ir	=bh_value[8] | bh_value[9] << 8;
}

bh1749.h

C/C++
/*
 * bh1749.h
 * twitter: @ArduinoEasy
 *
 */

#ifndef BH1749_H_
#define BH1749_H_

#include <stdbool.h>
#include <stdint.h>


#define BH1749_SYSTEM_CONTROL                           0x40
#define BH1749_MODE_CONTROL1                            0x41
#define BH1749_MODE_CONTROL2                            0x42
#define BH1749_RED_DATA_LSB                             0x50
#define BH1749_IR_DATA_LSB                             	0x58

#define BH1749_MODE_CONTROL2_RGB_EN_ENABLE              BIT(4)
#define BH1749_MODE_CONTROL1_DEFAULTS                   0x2A

typedef void (*Delay_ms_t)(uint32_t delay_ms);

typedef struct
{
	// Instance-specific properties
	uint16_t	bh_red;
	uint16_t	bh_green;
	uint16_t	bh_blue;
	uint16_t	bh_ir;
}BH_VALUES;

uint8_t init_bh1749(void);
void bh1479_read_values(BH_VALUES *bl_val);

#endif /* EASY_BH1749_H_ */

binary.h

C/C++
/*
  binary.h - Definitions for binary constants
  Copyright (c) 2006 David A. Mellis.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#ifndef Binary_h
#define Binary_h

#define B0 0
#define B00 0
#define B000 0
#define B0000 0
#define B00000 0
#define B000000 0
#define B0000000 0
#define B00000000 0
#define B1 1
#define B01 1
#define B001 1
#define B0001 1
#define B00001 1
#define B000001 1
#define B0000001 1
#define B00000001 1
#define B10 2
#define B010 2
#define B0010 2
#define B00010 2
#define B000010 2
#define B0000010 2
#define B00000010 2
#define B11 3
#define B011 3
#define B0011 3
#define B00011 3
#define B000011 3
#define B0000011 3
#define B00000011 3
#define B100 4
#define B0100 4
#define B00100 4
#define B000100 4
#define B0000100 4
#define B00000100 4
#define B101 5
#define B0101 5
#define B00101 5
#define B000101 5
#define B0000101 5
#define B00000101 5
#define B110 6
#define B0110 6
#define B00110 6
#define B000110 6
#define B0000110 6
#define B00000110 6
#define B111 7
#define B0111 7
#define B00111 7
#define B000111 7
#define B0000111 7
#define B00000111 7
#define B1000 8
#define B01000 8
#define B001000 8
#define B0001000 8
#define B00001000 8
#define B1001 9
#define B01001 9
#define B001001 9
#define B0001001 9
#define B00001001 9
#define B1010 10
#define B01010 10
#define B001010 10
#define B0001010 10
#define B00001010 10
#define B1011 11
#define B01011 11
#define B001011 11
#define B0001011 11
#define B00001011 11
#define B1100 12
#define B01100 12
#define B001100 12
#define B0001100 12
#define B00001100 12
#define B1101 13
#define B01101 13
#define B001101 13
#define B0001101 13
#define B00001101 13
#define B1110 14
#define B01110 14
#define B001110 14
#define B0001110 14
#define B00001110 14
#define B1111 15
#define B01111 15
#define B001111 15
#define B0001111 15
#define B00001111 15
#define B10000 16
#define B010000 16
#define B0010000 16
#define B00010000 16
#define B10001 17
#define B010001 17
#define B0010001 17
#define B00010001 17
#define B10010 18
#define B010010 18
#define B0010010 18
#define B00010010 18
#define B10011 19
#define B010011 19
#define B0010011 19
#define B00010011 19
#define B10100 20
#define B010100 20
#define B0010100 20
#define B00010100 20
#define B10101 21
#define B010101 21
#define B0010101 21
#define B00010101 21
#define B10110 22
#define B010110 22
#define B0010110 22
#define B00010110 22
#define B10111 23
#define B010111 23
#define B0010111 23
#define B00010111 23
#define B11000 24
#define B011000 24
#define B0011000 24
#define B00011000 24
#define B11001 25
#define B011001 25
#define B0011001 25
#define B00011001 25
#define B11010 26
#define B011010 26
#define B0011010 26
#define B00011010 26
#define B11011 27
#define B011011 27
#define B0011011 27
#define B00011011 27
#define B11100 28
#define B011100 28
#define B0011100 28
#define B00011100 28
#define B11101 29
#define B011101 29
#define B0011101 29
#define B00011101 29
#define B11110 30
#define B011110 30
#define B0011110 30
#define B00011110 30
#define B11111 31
#define B011111 31
#define B0011111 31
#define B00011111 31
#define B100000 32
#define B0100000 32
#define B00100000 32
#define B100001 33
#define B0100001 33
#define B00100001 33
#define B100010 34
#define B0100010 34
#define B00100010 34
#define B100011 35
#define B0100011 35
#define B00100011 35
#define B100100 36
#define B0100100 36
#define B00100100 36
#define B100101 37
#define B0100101 37
#define B00100101 37
#define B100110 38
#define B0100110 38
#define B00100110 38
#define B100111 39
#define B0100111 39
#define B00100111 39
#define B101000 40
#define B0101000 40
#define B00101000 40
#define B101001 41
#define B0101001 41
#define B00101001 41
#define B101010 42
#define B0101010 42
#define B00101010 42
#define B101011 43
#define B0101011 43
#define B00101011 43
#define B101100 44
#define B0101100 44
#define B00101100 44
#define B101101 45
#define B0101101 45
#define B00101101 45
#define B101110 46
#define B0101110 46
#define B00101110 46
#define B101111 47
#define B0101111 47
#define B00101111 47
#define B110000 48
#define B0110000 48
#define B00110000 48
#define B110001 49
#define B0110001 49
#define B00110001 49
#define B110010 50
#define B0110010 50
#define B00110010 50
#define B110011 51
#define B0110011 51
#define B00110011 51
#define B110100 52
#define B0110100 52
#define B00110100 52
#define B110101 53
#define B0110101 53
#define B00110101 53
#define B110110 54
#define B0110110 54
#define B00110110 54
#define B110111 55
#define B0110111 55
#define B00110111 55
#define B111000 56
#define B0111000 56
#define B00111000 56
#define B111001 57
#define B0111001 57
#define B00111001 57
#define B111010 58
#define B0111010 58
#define B00111010 58
#define B111011 59
#define B0111011 59
#define B00111011 59
#define B111100 60
#define B0111100 60
#define B00111100 60
#define B111101 61
#define B0111101 61
#define B00111101 61
#define B111110 62
#define B0111110 62
#define B00111110 62
#define B111111 63
#define B0111111 63
#define B00111111 63
#define B1000000 64
#define B01000000 64
#define B1000001 65
#define B01000001 65
#define B1000010 66
#define B01000010 66
#define B1000011 67
#define B01000011 67
#define B1000100 68
#define B01000100 68
#define B1000101 69
#define B01000101 69
#define B1000110 70
#define B01000110 70
#define B1000111 71
#define B01000111 71
#define B1001000 72
#define B01001000 72
#define B1001001 73
#define B01001001 73
#define B1001010 74
#define B01001010 74
#define B1001011 75
#define B01001011 75
#define B1001100 76
#define B01001100 76
#define B1001101 77
#define B01001101 77
#define B1001110 78
#define B01001110 78
#define B1001111 79
#define B01001111 79
#define B1010000 80
#define B01010000 80
#define B1010001 81
#define B01010001 81
#define B1010010 82
#define B01010010 82
#define B1010011 83
#define B01010011 83
#define B1010100 84
#define B01010100 84
#define B1010101 85
#define B01010101 85
#define B1010110 86
#define B01010110 86
#define B1010111 87
#define B01010111 87
#define B1011000 88
#define B01011000 88
#define B1011001 89
#define B01011001 89
#define B1011010 90
#define B01011010 90
#define B1011011 91
#define B01011011 91
#define B1011100 92
#define B01011100 92
#define B1011101 93
#define B01011101 93
#define B1011110 94
#define B01011110 94
#define B1011111 95
#define B01011111 95
#define B1100000 96
#define B01100000 96
#define B1100001 97
#define B01100001 97
#define B1100010 98
#define B01100010 98
#define B1100011 99
#define B01100011 99
#define B1100100 100
#define B01100100 100
#define B1100101 101
#define B01100101 101
#define B1100110 102
#define B01100110 102
#define B1100111 103
#define B01100111 103
#define B1101000 104
#define B01101000 104
#define B1101001 105
#define B01101001 105
#define B1101010 106
#define B01101010 106
#define B1101011 107
#define B01101011 107
#define B1101100 108
#define B01101100 108
#define B1101101 109
#define B01101101 109
#define B1101110 110
#define B01101110 110
#define B1101111 111
#define B01101111 111
#define B1110000 112
#define B01110000 112
#define B1110001 113
#define B01110001 113
#define B1110010 114
#define B01110010 114
#define B1110011 115
#define B01110011 115
#define B1110100 116
#define B01110100 116
#define B1110101 117
#define B01110101 117
#define B1110110 118
#define B01110110 118
#define B1110111 119
#define B01110111 119
#define B1111000 120
#define B01111000 120
#define B1111001 121
#define B01111001 121
#define B1111010 122
#define B01111010 122
#define B1111011 123
#define B01111011 123
#define B1111100 124
#define B01111100 124
#define B1111101 125
#define B01111101 125
#define B1111110 126
#define B01111110 126
#define B1111111 127
#define B01111111 127
#define B10000000 128
#define B10000001 129
#define B10000010 130
#define B10000011 131
#define B10000100 132
#define B10000101 133
#define B10000110 134
#define B10000111 135
#define B10001000 136
#define B10001001 137
#define B10001010 138
#define B10001011 139
#define B10001100 140
#define B10001101 141
#define B10001110 142
#define B10001111 143
#define B10010000 144
#define B10010001 145
#define B10010010 146
#define B10010011 147
#define B10010100 148
#define B10010101 149
#define B10010110 150
#define B10010111 151
#define B10011000 152
#define B10011001 153
#define B10011010 154
#define B10011011 155
#define B10011100 156
#define B10011101 157
#define B10011110 158
#define B10011111 159
#define B10100000 160
#define B10100001 161
#define B10100010 162
#define B10100011 163
#define B10100100 164
#define B10100101 165
#define B10100110 166
#define B10100111 167
#define B10101000 168
#define B10101001 169
#define B10101010 170
#define B10101011 171
#define B10101100 172
#define B10101101 173
#define B10101110 174
#define B10101111 175
#define B10110000 176
#define B10110001 177
#define B10110010 178
#define B10110011 179
#define B10110100 180
#define B10110101 181
#define B10110110 182
#define B10110111 183
#define B10111000 184
#define B10111001 185
#define B10111010 186
#define B10111011 187
#define B10111100 188
#define B10111101 189
#define B10111110 190
#define B10111111 191
#define B11000000 192
#define B11000001 193
#define B11000010 194
#define B11000011 195
#define B11000100 196
#define B11000101 197
#define B11000110 198
#define B11000111 199
#define B11001000 200
#define B11001001 201
#define B11001010 202
#define B11001011 203
#define B11001100 204
#define B11001101 205
#define B11001110 206
#define B11001111 207
#define B11010000 208
#define B11010001 209
#define B11010010 210
#define B11010011 211
#define B11010100 212
#define B11010101 213
#define B11010110 214
#define B11010111 215
#define B11011000 216
#define B11011001 217
#define B11011010 218
#define B11011011 219
#define B11011100 220
#define B11011101 221
#define B11011110 222
#define B11011111 223
#define B11100000 224
#define B11100001 225
#define B11100010 226
#define B11100011 227
#define B11100100 228
#define B11100101 229
#define B11100110 230
#define B11100111 231
#define B11101000 232
#define B11101001 233
#define B11101010 234
#define B11101011 235
#define B11101100 236
#define B11101101 237
#define B11101110 238
#define B11101111 239
#define B11110000 240
#define B11110001 241
#define B11110010 242
#define B11110011 243
#define B11110100 244
#define B11110101 245
#define B11110110 246
#define B11110111 247
#define B11111000 248
#define B11111001 249
#define B11111010 250
#define B11111011 251
#define B11111100 252
#define B11111101 253
#define B11111110 254
#define B11111111 255

#endif

glcdfont.c

C/C++
#ifndef FONT5X7_H
#define FONT5X7_H


#define PROGMEM


// Standard ASCII 5x7 font

static const unsigned char font[] PROGMEM = {
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x3E, 0x5B, 0x4F, 0x5B, 0x3E,
    0x3E, 0x6B, 0x4F, 0x6B, 0x3E,
    0x1C, 0x3E, 0x7C, 0x3E, 0x1C,
    0x18, 0x3C, 0x7E, 0x3C, 0x18,
    0x1C, 0x57, 0x7D, 0x57, 0x1C,
    0x1C, 0x5E, 0x7F, 0x5E, 0x1C,
    0x00, 0x18, 0x3C, 0x18, 0x00,
    0xFF, 0xE7, 0xC3, 0xE7, 0xFF,
    0x00, 0x18, 0x24, 0x18, 0x00,
    0xFF, 0xE7, 0xDB, 0xE7, 0xFF,
    0x30, 0x48, 0x3A, 0x06, 0x0E,
    0x26, 0x29, 0x79, 0x29, 0x26,
    0x40, 0x7F, 0x05, 0x05, 0x07,
    0x40, 0x7F, 0x05, 0x25, 0x3F,
    0x5A, 0x3C, 0xE7, 0x3C, 0x5A,
    0x7F, 0x3E, 0x1C, 0x1C, 0x08,
    0x08, 0x1C, 0x1C, 0x3E, 0x7F,
    0x14, 0x22, 0x7F, 0x22, 0x14,
    0x5F, 0x5F, 0x00, 0x5F, 0x5F,
    0x06, 0x09, 0x7F, 0x01, 0x7F,
    0x00, 0x66, 0x89, 0x95, 0x6A,
    0x60, 0x60, 0x60, 0x60, 0x60,
    0x94, 0xA2, 0xFF, 0xA2, 0x94,
    0x08, 0x04, 0x7E, 0x04, 0x08,
    0x10, 0x20, 0x7E, 0x20, 0x10,
    0x08, 0x08, 0x2A, 0x1C, 0x08,
    0x08, 0x1C, 0x2A, 0x08, 0x08,
    0x1E, 0x10, 0x10, 0x10, 0x10,
    0x0C, 0x1E, 0x0C, 0x1E, 0x0C,
    0x30, 0x38, 0x3E, 0x38, 0x30,
    0x06, 0x0E, 0x3E, 0x0E, 0x06,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x5F, 0x00, 0x00,
    0x00, 0x07, 0x00, 0x07, 0x00,
    0x14, 0x7F, 0x14, 0x7F, 0x14,
    0x24, 0x2A, 0x7F, 0x2A, 0x12,
    0x23, 0x13, 0x08, 0x64, 0x62,
    0x36, 0x49, 0x56, 0x20, 0x50,
    0x00, 0x08, 0x07, 0x03, 0x00,
    0x00, 0x1C, 0x22, 0x41, 0x00,
    0x00, 0x41, 0x22, 0x1C, 0x00,
    0x2A, 0x1C, 0x7F, 0x1C, 0x2A,
    0x08, 0x08, 0x3E, 0x08, 0x08,
    0x00, 0x80, 0x70, 0x30, 0x00,
    0x08, 0x08, 0x08, 0x08, 0x08,
    0x00, 0x00, 0x60, 0x60, 0x00,
    0x20, 0x10, 0x08, 0x04, 0x02,
    0x3E, 0x51, 0x49, 0x45, 0x3E,
    0x00, 0x42, 0x7F, 0x40, 0x00,
    0x72, 0x49, 0x49, 0x49, 0x46,
    0x21, 0x41, 0x49, 0x4D, 0x33,
    0x18, 0x14, 0x12, 0x7F, 0x10,
    0x27, 0x45, 0x45, 0x45, 0x39,
    0x3C, 0x4A, 0x49, 0x49, 0x31,
    0x41, 0x21, 0x11, 0x09, 0x07,
    0x36, 0x49, 0x49, 0x49, 0x36,
    0x46, 0x49, 0x49, 0x29, 0x1E,
    0x00, 0x00, 0x14, 0x00, 0x00,
    0x00, 0x40, 0x34, 0x00, 0x00,
    0x00, 0x08, 0x14, 0x22, 0x41,
    0x14, 0x14, 0x14, 0x14, 0x14,
    0x00, 0x41, 0x22, 0x14, 0x08,
    0x02, 0x01, 0x59, 0x09, 0x06,
    0x3E, 0x41, 0x5D, 0x59, 0x4E,
    0x7C, 0x12, 0x11, 0x12, 0x7C,
    0x7F, 0x49, 0x49, 0x49, 0x36,
    0x3E, 0x41, 0x41, 0x41, 0x22,
    0x7F, 0x41, 0x41, 0x41, 0x3E,
    0x7F, 0x49, 0x49, 0x49, 0x41,
    0x7F, 0x09, 0x09, 0x09, 0x01,
    0x3E, 0x41, 0x41, 0x51, 0x73,
    0x7F, 0x08, 0x08, 0x08, 0x7F,
    0x00, 0x41, 0x7F, 0x41, 0x00,
    0x20, 0x40, 0x41, 0x3F, 0x01,
    0x7F, 0x08, 0x14, 0x22, 0x41,
    0x7F, 0x40, 0x40, 0x40, 0x40,
    0x7F, 0x02, 0x1C, 0x02, 0x7F,
    0x7F, 0x04, 0x08, 0x10, 0x7F,
    0x3E, 0x41, 0x41, 0x41, 0x3E,
    0x7F, 0x09, 0x09, 0x09, 0x06,
    0x3E, 0x41, 0x51, 0x21, 0x5E,
    0x7F, 0x09, 0x19, 0x29, 0x46,
    0x26, 0x49, 0x49, 0x49, 0x32,
    0x03, 0x01, 0x7F, 0x01, 0x03,
    0x3F, 0x40, 0x40, 0x40, 0x3F,
    0x1F, 0x20, 0x40, 0x20, 0x1F,
    0x3F, 0x40, 0x38, 0x40, 0x3F,
    0x63, 0x14, 0x08, 0x14, 0x63,
    0x03, 0x04, 0x78, 0x04, 0x03,
    0x61, 0x59, 0x49, 0x4D, 0x43,
    0x00, 0x7F, 0x41, 0x41, 0x41,
    0x02, 0x04, 0x08, 0x10, 0x20,
    0x00, 0x41, 0x41, 0x41, 0x7F,
    0x04, 0x02, 0x01, 0x02, 0x04,
    0x40, 0x40, 0x40, 0x40, 0x40,
    0x00, 0x03, 0x07, 0x08, 0x00,
    0x20, 0x54, 0x54, 0x78, 0x40,
    0x7F, 0x28, 0x44, 0x44, 0x38,
    0x38, 0x44, 0x44, 0x44, 0x28,
    0x38, 0x44, 0x44, 0x28, 0x7F,
    0x38, 0x54, 0x54, 0x54, 0x18,
    0x00, 0x08, 0x7E, 0x09, 0x02,
    0x18, 0xA4, 0xA4, 0x9C, 0x78,
    0x7F, 0x08, 0x04, 0x04, 0x78,
    0x00, 0x44, 0x7D, 0x40, 0x00,
    0x20, 0x40, 0x40, 0x3D, 0x00,
    0x7F, 0x10, 0x28, 0x44, 0x00,
    0x00, 0x41, 0x7F, 0x40, 0x00,
    0x7C, 0x04, 0x78, 0x04, 0x78,
    0x7C, 0x08, 0x04, 0x04, 0x78,
    0x38, 0x44, 0x44, 0x44, 0x38,
    0xFC, 0x18, 0x24, 0x24, 0x18,
    0x18, 0x24, 0x24, 0x18, 0xFC,
    0x7C, 0x08, 0x04, 0x04, 0x08,
    0x48, 0x54, 0x54, 0x54, 0x24,
    0x04, 0x04, 0x3F, 0x44, 0x24,
    0x3C, 0x40, 0x40, 0x20, 0x7C,
    0x1C, 0x20, 0x40, 0x20, 0x1C,
    0x3C, 0x40, 0x30, 0x40, 0x3C,
    0x44, 0x28, 0x10, 0x28, 0x44,
    0x4C, 0x90, 0x90, 0x90, 0x7C,
    0x44, 0x64, 0x54, 0x4C, 0x44,
    0x00, 0x08, 0x36, 0x41, 0x00,
    0x00, 0x00, 0x77, 0x00, 0x00,
    0x00, 0x41, 0x36, 0x08, 0x00,
    0x02, 0x01, 0x02, 0x04, 0x02,
    0x3C, 0x26, 0x23, 0x26, 0x3C,
    0x1E, 0xA1, 0xA1, 0x61, 0x12,
    0x3A, 0x40, 0x40, 0x20, 0x7A,
    0x38, 0x54, 0x54, 0x55, 0x59,
    0x21, 0x55, 0x55, 0x79, 0x41,
    0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut
    0x21, 0x55, 0x54, 0x78, 0x40,
    0x20, 0x54, 0x55, 0x79, 0x40,
    0x0C, 0x1E, 0x52, 0x72, 0x12,
    0x39, 0x55, 0x55, 0x55, 0x59,
    0x39, 0x54, 0x54, 0x54, 0x59,
    0x39, 0x55, 0x54, 0x54, 0x58,
    0x00, 0x00, 0x45, 0x7C, 0x41,
    0x00, 0x02, 0x45, 0x7D, 0x42,
    0x00, 0x01, 0x45, 0x7C, 0x40,
    0x7D, 0x12, 0x11, 0x12, 0x7D, // A-umlaut
    0xF0, 0x28, 0x25, 0x28, 0xF0,
    0x7C, 0x54, 0x55, 0x45, 0x00,
    0x20, 0x54, 0x54, 0x7C, 0x54,
    0x7C, 0x0A, 0x09, 0x7F, 0x49,
    0x32, 0x49, 0x49, 0x49, 0x32,
    0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut
    0x32, 0x4A, 0x48, 0x48, 0x30,
    0x3A, 0x41, 0x41, 0x21, 0x7A,
    0x3A, 0x42, 0x40, 0x20, 0x78,
    0x00, 0x9D, 0xA0, 0xA0, 0x7D,
    0x3D, 0x42, 0x42, 0x42, 0x3D, // O-umlaut
    0x3D, 0x40, 0x40, 0x40, 0x3D,
    0x3C, 0x24, 0xFF, 0x24, 0x24,
    0x48, 0x7E, 0x49, 0x43, 0x66,
    0x2B, 0x2F, 0xFC, 0x2F, 0x2B,
    0xFF, 0x09, 0x29, 0xF6, 0x20,
    0xC0, 0x88, 0x7E, 0x09, 0x03,
    0x20, 0x54, 0x54, 0x79, 0x41,
    0x00, 0x00, 0x44, 0x7D, 0x41,
    0x30, 0x48, 0x48, 0x4A, 0x32,
    0x38, 0x40, 0x40, 0x22, 0x7A,
    0x00, 0x7A, 0x0A, 0x0A, 0x72,
    0x7D, 0x0D, 0x19, 0x31, 0x7D,
    0x26, 0x29, 0x29, 0x2F, 0x28,
    0x26, 0x29, 0x29, 0x29, 0x26,
    0x30, 0x48, 0x4D, 0x40, 0x20,
    0x38, 0x08, 0x08, 0x08, 0x08,
    0x08, 0x08, 0x08, 0x08, 0x38,
    0x2F, 0x10, 0xC8, 0xAC, 0xBA,
    0x2F, 0x10, 0x28, 0x34, 0xFA,
    0x00, 0x00, 0x7B, 0x00, 0x00,
    0x08, 0x14, 0x2A, 0x14, 0x22,
    0x22, 0x14, 0x2A, 0x14, 0x08,
    0xAA, 0x00, 0x55, 0x00, 0xAA,
    0xAA, 0x55, 0xAA, 0x55, 0xAA,
    0x00, 0x00, 0x00, 0xFF, 0x00,
    0x10, 0x10, 0x10, 0xFF, 0x00,
    0x14, 0x14, 0x14, 0xFF, 0x00,
    0x10, 0x10, 0xFF, 0x00, 0xFF,
    0x10, 0x10, 0xF0, 0x10, 0xF0,
    0x14, 0x14, 0x14, 0xFC, 0x00,
    0x14, 0x14, 0xF7, 0x00, 0xFF,
    0x00, 0x00, 0xFF, 0x00, 0xFF,
    0x14, 0x14, 0xF4, 0x04, 0xFC,
    0x14, 0x14, 0x17, 0x10, 0x1F,
    0x10, 0x10, 0x1F, 0x10, 0x1F,
    0x14, 0x14, 0x14, 0x1F, 0x00,
    0x10, 0x10, 0x10, 0xF0, 0x00,
    0x00, 0x00, 0x00, 0x1F, 0x10,
    0x10, 0x10, 0x10, 0x1F, 0x10,
    0x10, 0x10, 0x10, 0xF0, 0x10,
    0x00, 0x00, 0x00, 0xFF, 0x10,
    0x10, 0x10, 0x10, 0x10, 0x10,
    0x10, 0x10, 0x10, 0xFF, 0x10,
    0x00, 0x00, 0x00, 0xFF, 0x14,
    0x00, 0x00, 0xFF, 0x00, 0xFF,
    0x00, 0x00, 0x1F, 0x10, 0x17,
    0x00, 0x00, 0xFC, 0x04, 0xF4,
    0x14, 0x14, 0x17, 0x10, 0x17,
    0x14, 0x14, 0xF4, 0x04, 0xF4,
    0x00, 0x00, 0xFF, 0x00, 0xF7,
    0x14, 0x14, 0x14, 0x14, 0x14,
    0x14, 0x14, 0xF7, 0x00, 0xF7,
    0x14, 0x14, 0x14, 0x17, 0x14,
    0x10, 0x10, 0x1F, 0x10, 0x1F,
    0x14, 0x14, 0x14, 0xF4, 0x14,
    0x10, 0x10, 0xF0, 0x10, 0xF0,
    0x00, 0x00, 0x1F, 0x10, 0x1F,
    0x00, 0x00, 0x00, 0x1F, 0x14,
    0x00, 0x00, 0x00, 0xFC, 0x14,
    0x00, 0x00, 0xF0, 0x10, 0xF0,
    0x10, 0x10, 0xFF, 0x10, 0xFF,
    0x14, 0x14, 0x14, 0xFF, 0x14,
    0x10, 0x10, 0x10, 0x1F, 0x00,
    0x00, 0x00, 0x00, 0xF0, 0x10,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
    0xFF, 0xFF, 0xFF, 0x00, 0x00,
    0x00, 0x00, 0x00, 0xFF, 0xFF,
    0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
    0x38, 0x44, 0x44, 0x38, 0x44,
    0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta
    0x7E, 0x02, 0x02, 0x06, 0x06,
    0x02, 0x7E, 0x02, 0x7E, 0x02,
    0x63, 0x55, 0x49, 0x41, 0x63,
    0x38, 0x44, 0x44, 0x3C, 0x04,
    0x40, 0x7E, 0x20, 0x1E, 0x20,
    0x06, 0x02, 0x7E, 0x02, 0x02,
    0x99, 0xA5, 0xE7, 0xA5, 0x99,
    0x1C, 0x2A, 0x49, 0x2A, 0x1C,
    0x4C, 0x72, 0x01, 0x72, 0x4C,
    0x30, 0x4A, 0x4D, 0x4D, 0x30,
    0x30, 0x48, 0x78, 0x48, 0x30,
    0xBC, 0x62, 0x5A, 0x46, 0x3D,
    0x3E, 0x49, 0x49, 0x49, 0x00,
    0x7E, 0x01, 0x01, 0x01, 0x7E,
    0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
    0x44, 0x44, 0x5F, 0x44, 0x44,
    0x40, 0x51, 0x4A, 0x44, 0x40,
    0x40, 0x44, 0x4A, 0x51, 0x40,
    0x00, 0x00, 0xFF, 0x01, 0x03,
    0xE0, 0x80, 0xFF, 0x00, 0x00,
    0x08, 0x08, 0x6B, 0x6B, 0x08,
    0x36, 0x12, 0x36, 0x24, 0x36,
    0x06, 0x0F, 0x09, 0x0F, 0x06,
    0x00, 0x00, 0x18, 0x18, 0x00,
    0x00, 0x00, 0x10, 0x10, 0x00,
    0x30, 0x40, 0xFF, 0x01, 0x01,
    0x00, 0x1F, 0x01, 0x01, 0x1E,
    0x00, 0x19, 0x1D, 0x17, 0x12,
    0x00, 0x3C, 0x3C, 0x3C, 0x3C,
    0x00, 0x00, 0x00, 0x00, 0x00
};
#endif // FONT5X7_H

Credits

Mahmood ul Hassan

Mahmood ul Hassan

13 projects • 18 followers
Electronics Engineer with more than 13 years of experience in reverse engineering and test & measurement equipment designing

Comments