Created
October 28, 2018 17:42
-
-
Save soemarko/7af13d2d31edb54bd3f6ca529cf79fca to your computer and use it in GitHub Desktop.
Source code for DIY O2 Analyzer, read the introduction here: http://soemarko.com/blog/diy-o2-analyzer-part-1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <SPI.h> | |
#include <Wire.h> | |
#include <Piccolino_OLED.h> | |
#include <Piccolino_RAM.h> | |
#include <EEPROM.h> | |
#include <Adafruit_ADS1015.h> | |
#include <RunningAverage.h> | |
const int buttonPin=3; // push button | |
#define RA_SIZE 20 | |
RunningAverage RA(RA_SIZE); | |
Adafruit_ADS1115 ads(0x48); | |
PROGMEM const unsigned char logo [] = { | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x01, 0x37, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x09, 0xb7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x9d, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x9f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x01, 0xdf, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x60, 0x00, 0x00, 0x13, 0xff, 0x17, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x78, 0x10, 0x00, 0x93, 0xf8, 0x97, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x7c, 0x12, 0x00, 0xb7, 0xe3, 0x97, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x7e, 0x36, 0xa1, 0xbf, 0xcf, 0xb7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x7e, 0x37, 0xed, 0xfe, 0xdf, 0xb7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x7e, 0x3f, 0xff, 0xf8, 0xdf, 0xb7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x3e, 0x3f, 0xff, 0xe6, 0x1e, 0x37, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x0e, 0x3f, 0xff, 0x9e, 0xd8, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0x7e, 0xc7, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xfe, 0x7e, 0xdf, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0xff, 0xfe, 0x7c, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0xff, 0xfe, 0x71, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0xff, 0xff, 0x47, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0xff, 0xff, 0x1f, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0xff, 0xff, 0x7f, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xff, 0xfe, 0x0e, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff, 0xf8, 0x71, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x7f, 0xc0, 0x66, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x1f, 0x00, 0xd8, 0x0d, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x04, 0x03, 0xde, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x01, 0xdf, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0xdf, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x03, 0x00, 0x00, 0xdf, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0x03, 0x00, 0xdf, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0x02, 0x01, 0x1f, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x87, 0x02, 0x07, 0x1f, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xe7, 0x02, 0x07, 0x27, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x02, 0x0f, 0x39, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x03, 0xff, 0x3e, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff}; | |
Piccolino_OLED display; | |
Piccolino_RAM ram; | |
int calibrationv; | |
float multiplier; | |
const int EEPROM_addr = 0; | |
const int cal_holdTime = 1; // 2 sec button hold to calibration | |
long secs_held; // How long the button was held (seconds) | |
byte previous = HIGH; | |
unsigned long firstTime; // how long since the button was first pressed | |
int active = 0; | |
double result_max = 0; | |
/* | |
Calculate MOD (Maximum Operating Depth) | |
*/ | |
const float max_po1 = 1.40; | |
const float max_po2 = 1.60; | |
float cal_mod(float percentage, float ppo2 = 1.4) { | |
return 10 * ((ppo2 / (percentage / 100)) - 1); | |
} | |
void drawBitmap(int16_t x, int16_t y, | |
const unsigned char * bitmap, int16_t w, int16_t h) { | |
int16_t i, j, byteWidth = (w + 7) / 8; | |
display.clear(); | |
for (j = 0; j < h; j++) { | |
for (i = 0; i < w; i++) { | |
if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) { | |
display.drawPixel(x + i, y + j, WHITE); | |
} | |
} | |
} | |
display.update(); | |
} | |
void read_sensor() { | |
int16_t millivolts = 0; | |
millivolts = ads.readADC_Differential_0_1(); | |
// Serial.println(millivolts); | |
RA.addValue(millivolts); | |
} | |
void calibrate() { | |
display.clear(); | |
display.setTextColor(WHITE); | |
display.setCursor(0, 0); | |
display.setTextSize(1); | |
display.print(F("Calibrating...")); | |
display.update(); | |
active = 0; | |
result_max = 0.0; | |
double avg = RA.getAverage(); | |
calibrationv = abs(avg); | |
EEPROMWriteInt(EEPROM_addr, calibrationv); // write to eeprom | |
// Serial.print(">>>>>> "); | |
// Serial.println(calibrationv); | |
delay(1000); | |
} | |
void analyzing() { | |
double currentmv = 0; | |
double result; | |
double mv = 0.0; | |
read_sensor(); | |
currentmv = RA.getAverage(); | |
currentmv = abs(currentmv); | |
// Serial.print(">> "); | |
// Serial.println(currentmv); | |
result = (currentmv / calibrationv) * 20.9; | |
if (result > 99.9) result = 99.9; | |
mv = currentmv * multiplier; | |
display.clear(); | |
display.setTextColor(WHITE); | |
display.setCursor(0, 0); | |
if (mv < 0.02 || result <= 0) { | |
display.setTextSize(2); | |
display.println(F("Sensor")); | |
display.print(F("Error!")); | |
} else { | |
display.setTextSize(4); | |
display.print(result, 1); | |
display.println(F("%")); | |
if (result >= result_max) { | |
result_max = result; | |
} | |
display.setTextSize(1); | |
display.setCursor(0, 31); | |
display.setTextColor(BLACK, WHITE); | |
display.print(F("Max ")); | |
display.print(result_max, 1); | |
display.print(F("% ")); | |
display.print(mv, 2); | |
display.print(F("mv")); | |
if (active % 4) { | |
display.setCursor(115, 29); | |
display.setTextColor(WHITE); | |
display.print(F(".")); | |
} | |
display.setTextColor(WHITE); | |
display.setCursor(0, 40); | |
display.print(F("pO2 ")); | |
display.print(max_po1, 1); | |
display.print(F("/")); | |
display.print(max_po2, 1); | |
display.print(F(" MOD")); | |
display.setTextSize(2); | |
display.setCursor(0, 50); | |
display.print(cal_mod(result, max_po1), 1); | |
display.print(F("/")); | |
display.print(cal_mod(result, max_po2), 1); | |
display.print(F("m ")); | |
} | |
display.update(); | |
} | |
void EEPROMWriteInt(int p_address, int p_value) { | |
byte lowByte = ((p_value >> 0) & 0xFF); | |
byte highByte = ((p_value >> 8) & 0xFF); | |
EEPROM.write(p_address, lowByte); | |
EEPROM.write(p_address + 1, highByte); | |
} | |
unsigned int EEPROMReadInt(int p_address) { | |
byte lowByte = EEPROM.read(p_address); | |
byte highByte = EEPROM.read(p_address + 1); | |
return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00); | |
} | |
void setup() { | |
// put your setup code here, to run once: | |
display.begin(); | |
ram.begin(); | |
// Serial.begin(9600); | |
drawBitmap(0, 0, logo, 128, 64); | |
pinMode(buttonPin, INPUT_PULLUP); | |
ads.setGain(GAIN_TWO); | |
multiplier = 0.0625F; | |
ads.begin(); // ads1115 start | |
//calibrate | |
for (int cx = 0; cx < RA_SIZE; cx++) { | |
read_sensor(); | |
delay(50); | |
} | |
calibrationv = EEPROMReadInt(EEPROM_addr); | |
if (calibrationv < 100) { | |
calibrate(); | |
} | |
// delay(1000); | |
} | |
void loop() { | |
// put your main code here, to run repeatedly: | |
int current = digitalRead(buttonPin); | |
if (current == LOW && previous == HIGH && (millis() - firstTime) > 200) { | |
firstTime = millis(); | |
active = 17; | |
} | |
secs_held = (millis() - firstTime) / 1000; | |
if (secs_held > 0 && current == LOW && previous == LOW) { | |
if (secs_held >= cal_holdTime) { | |
calibrate(); | |
} | |
} | |
previous = current; | |
analyzing(); | |
delay(200); | |
active++; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment