Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
nberlette committed Sep 25, 2021
0 parents commit cc2caf3
Show file tree
Hide file tree
Showing 7 changed files with 485 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.other
*.pem
.cache
*.log
*.workspace
*.code-workspace
*.*-workspace
.npm
.yarn
.pnpm
.env.local
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2021 Nicholas Berlette <nick@berlette.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# `ECA: Ethanol Content Analyzer`

Ethanol Content Analyzer for Arduino Nano or Uno.

Converts digital FlexFuel sensor data (`50~150 Hz`) into analog (`0-5v` or `0.5-4.5v`), for tuners, datalogs, and in-dash gauges.

## `Pins`

| Type | Pin | Description |
| ---------- | -------- | ------------------- |
| Sensor In | `D8` | `TIMER1 / ICP1` |
| PWM Output | `D3/D11` | Built-in PWM driver |
| DAC Output | `A4/A5` | MCP4725 12bit DAC |

## `Hz -> E % -> V`

| Input (Hz) | E (%) | Output (V) |
| :--------- | :----: | :------------------------- |
| `50 hz` | ` 0 %` | `0.50v` |
| `100 hz` | `50 %` | `2.25v` |
| `150 hz` | `100%` | `4.50v` |
| **Errors** | | |
| `< 50 hz` | `---` | `4.80v` - contaminated |
| `> 150 hz` | `---` | `4.90v` - high water level |
| `<= 0 hz` | `---` | `0.10v` - disconnected |

## `License`

[MIT](https://mit-license.org) © [Nicholas Berlette](https://nick.berlette.com)
148 changes: 148 additions & 0 deletions libraries/MCP4725/MCP4725.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#include "Arduino.h"
#include <Wire.h>
#include "MCP4725.h"
/*See page 19 of the MCP4726 DataSheet.
*[C2,C1]=00 -- Fast Mode
*[C2,C1,C0]=010 --- Write to DAC register only
*[C2,C1,C0]=011 --- Write to DAC and EEPROM registers
*[PD1,PD0]=00 -- Power Normal mode
*[PD1,PD0]=01 -- Power Down mode with 1k pull down
*[PD1,PD0]=10 -- Power Down mode with 100k pull down
*[PD1,PD0]=11 -- Power Down mode with 500k pull down
*The contents of bytes 2nd,3rd,4th are orgnized differently
*based on whether the DAC is operated in Normal-Speed or Fast-Speed. Let
*D denote data byte, x denote don't-care. Then:
*
*For Normal-Speed:
*[Addr.Byte]+[C2,C1,C0,x,x,PD1,PD0,x]+[D11,D10,D9,D8,D7,D6,D5,D4]+[D3,D2,D1,D0,x,x,x,x]
*
*For Fast-Speed:
*[Addr.Byte]+[C2,C1,PD1,PD0,D11,D10,D9,D8],[D7,D6,D5,D4,D3,D2,D1,D0]
*
*The address byte for our dac is (1,1,0,0,0,1,A0). By default A0=GND, but we can connect it to VCC.
*For A0=GND the hex value of address is 0x62 (1100010)
*For A0=VCC the hex value of address is 0x63 (1100011)
*We must pass the address in our Arduino code in the argument of the function .begin(). But the argument must
*be an 8-bit value and the address is only 7-bit long. What is happening internally is left shifting by 1 bit.
*You can see this if you trace the definition of the .begin() function and the definition of the twi_setAddress()
*in the file twi.c. The register TWAR is set to (address << 1). The R/W bit (bit 8) is actually determined based
*on the function you send after .begin()
*/
MCP4725::MCP4725() {}
void MCP4725::begin(uint8_t addr) { //in the Arduino code, we'd send 0x62 as arumenment when A0_bit=GND (default).
_i2caddr = addr; //or if A0_bit=vcc then we would pass 0x63 as argument
Wire.begin();
}
void MCP4725::setFastMode(){
#ifdef TWBR // in case this library is used on a chip that does not have TWBR reg defined.
TWBR = ((F_CPU / 400000L) - 16) / 2; // Set I2C frequency for the ATMega to 400kHz
#endif
}
/*
*Wire.write() takes an 8-bit unsigned int. We thus need to convert our 16-bit
*argument to 2 8-bit variables. 16 to 8-bit, conversion keeps the 8 LSBs.
*/
void MCP4725::setVoltageAndSave(uint16_t output){
/*For Normal-Speed: (Only normal speed includes the C3 bit, needed for writing to EEPROM)
*[Addr.Byte]+[C2,C1,C0,x,x,PD1,PD0,x]+[D11,D10,D9,D8,D7,D6,D5,D4]+[D3,D2,D1,D0,x,x,x,x] */
Wire.beginTransmission(_i2caddr);
Wire.write(WRITEDACEEPROM); //[C2,C1,C0,x,x,PD1,PD0,x]=[0,1,1,0,0,0,0,0]
/*The 12-bit output is in 16-bit form (0,0,0,0,D11.D10.D9.D8.D7.D6.D5.D4,D3.D2.D1.D0).*/
uint8_t firstbyte=(output>>4);//(0,0,0,0,0,0,0,0,D11.D10.D9.D8.D7.D6.D5.D4) of which only the 8 LSB's survive
output = output << 12; //(D3.D2.D1.D0,0,0,0,0,0,0,0,0,0,0,0,0)
uint8_t secndbyte=(output>>8);//(0,0,0,0,0,0,0,0,D3,D2,D1,D0,0,0,0,0) of which only the 8 LSB's survive.
Wire.write(firstbyte);
Wire.write(secndbyte);
Wire.endTransmission();
}
void MCP4725::setVoltage(uint16_t output){
/*For Normal-Speed:
*[Addr.Byte]+[C2,C1,C0,x,x,PD1,PD0,x]+[D11,D10,D9,D8,D7,D6,D5,D4]+[D3,D2,D1,D0,x,x,x,x] */
Wire.beginTransmission(_i2caddr);
Wire.write(WRITEDAC); //[C2,C1,C0,x,x,PD1,PD0,x]=[0,1,0,0,0,0,0,0]
/*The 12-bit output is in 16-bit form (0,0,0,0,D11.D10.D9.D8.D7.D6.D5.D4,D3.D2.D1.D0).*/
uint8_t firstbyte=(output>>4);//(0,0,0,0,0,0,0,0,D11.D10.D9.D8.D7.D6.D5.D4) of which only the 8 LSB's survive
output = output << 12; //(D3.D2.D1.D0,0,0,0,0,0,0,0,0,0,0,0,0)
uint8_t secndbyte=(output>>8);//(0,0,0,0,0,0,0,0,D3,D2,D1,D0,0,0,0,0) of which only the 8 LSB's survive.
Wire.write(firstbyte);
Wire.write(secndbyte);
Wire.endTransmission();
}
void MCP4725::setVoltageFast( uint16_t output){
/*For Fast-Speed:
*[Addr.Byte]+[C2,C1,PD1,PD0,D11,D10,D9,D8],[D7,D6,D5,D4,D3,D2,D1,D0] */
Wire.beginTransmission(_i2caddr);
//output is a 12-bit value in 16-bit form, namely: [0,0,0,0,D11,D10,D9,D8,D7,D6,D5,D4,D3,D2,D1,D0]
uint8_t firstbyte=(output>>8); //[0,0,0,0,0,0,0,0,0,0,0,0,D11,D10,D9,D8] only the 8 LSB's survive
uint8_t secndbyte=(output); //only the 8 LSB's survive.
Wire.write(firstbyte); // Upper data bits (0,0,0,0,D11,D10,D9,D8)
Wire.write(secndbyte); // Lower data bits (D7,D6,D5,D4,D3,D2,D1,D0)
Wire.endTransmission();
}
void MCP4725::powerDown1kPullDown(){ //[PD1,PD0]=01; [C2,C1,C0]=010 - Write to DAC only
/*For Normal-Speed:
*[Addr.Byte]+[C2,C1,C0,x,x,PD1,PD0,x]+[D11,D10,D9,D8,D7,D6,D5,D4]+[D3,D2,D1,D0,x,x,x,x] */
Wire.beginTransmission(_i2caddr);
Wire.write(0b01000010);
Wire.write(0b00000000);
Wire.write(0b00000000);
Wire.endTransmission();
}
void MCP4725::powerDown100kPullDown(){//[PD1,PD0]=10; [C2,C1,C0]=010 - Write to DAC only
/*For Normal-Speed:
*[Addr.Byte]+[C2,C1,C0,x,x,PD1,PD0,x]+[D11,D10,D9,D8,D7,D6,D5,D4]+[D3,D2,D1,D0,x,x,x,x] */
Wire.beginTransmission(_i2caddr);
Wire.write(0b01000100);
Wire.write(0b00000000);
Wire.write(0b00000000);
Wire.endTransmission();
}
void MCP4725::powerDown500kPullDown(){//[PD1,PD0]=11; [C2,C1,C0]=010 - Write to DAC only
/*For Normal-Speed:
*[Addr.Byte]+[C2,C1,C0,x,x,PD1,PD0,x]+[D11,D10,D9,D8,D7,D6,D5,D4]+[D3,D2,D1,D0,x,x,x,x] */
Wire.beginTransmission(_i2caddr);
Wire.write(0b01000110);
Wire.write(0b00000000);
Wire.write(0b00000000);
Wire.endTransmission();
}

uint16_t MCP4725::readCurrentDacVal(){
Wire.requestFrom(_i2caddr, (uint8_t) 5);
while(Wire.available()!=5){
Serial.println("Waiting for readValFromEEPROM() to complete.");
//just wait for a while until the DAC sends the data to the reabuffer
}
Wire.read(); //status
uint8_t upper8bits = Wire.read(); //DAC Register data (D11,D10,D9,D8,D7,D6,D5,D4)
uint8_t lower8bits = Wire.read(); //DAC Register data (D3,D2,D1,D0,0,0,0,0)
Wire.read(); //forth returned byte is EEPROM data (x,PD1,PD0,x,D11,D10,D9,D8)
Wire.read(); //fifth returned byte is EEPROM data (D7,D6,D5,D4,D3,D2,D1,D0)

/*We now need to return the value (0,0,0,0,D11,D10,D9,D8,D7,D6,D5,D4,D3,D2,D1,D0). */
return (upper8bits<<4) | (lower8bits>>4); //This is how we get the 16-bit result we want to return.
}


uint16_t MCP4725::readValFromEEPROM(){
Wire.requestFrom(_i2caddr, (uint8_t) 5);
while(Wire.available()!=5){
Serial.println("Waiting for readValFromEEPROM() to complete.");
//just wait for a while until the DAC sends the data to the reabuffer
}
uint8_t statusBit = Wire.read() >> 7;
Wire.read(); //secnd returned byte is DAC Register data (upper 8 bits)
Wire.read(); //third returned byte is DAC Register data (lower 4 bits + 0000)
uint8_t upper8bits = Wire.read(); //forth returned byte is EEPROM data (x,PD1,PD0,x,D11,D10,D9,D8)
uint8_t lower8bits = Wire.read(); //fifth returned byte is EEPROM data (D7,D6,D5,D4,D3,D2,D1,D0)

if(statusBit==0){
Serial.println("Currently writing to EEPROM. Trying to read again...");
return readValFromEEPROM();
}
else{
/*We now need to return the value (0,0,0,0,D11,D10,D9,D8,D7,D6,D5,D4,D3,D2,D1,D0). */
upper8bits = upper8bits & 0b00001111; //clear the first 4 bits.
return (upper8bits<<8) | lower8bits; //This is how we get the 16-bit result we want to return.
}
}
22 changes: 22 additions & 0 deletions libraries/MCP4725/MCP4725.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "Arduino.h"
#include <Wire.h>

#define WRITEDAC (0x40) //(0100 0000) Writes to the DAC
#define WRITEDACEEPROM (0x60) //(0110 0000) Writes to the DAC and the EEPROM (persisting the assigned value after reset)

class MCP4725{
public:
MCP4725();
void begin(uint8_t a);
void setFastMode();
void setVoltageAndSave(uint16_t output);
void setVoltage(uint16_t output);
void setVoltageFast(uint16_t output);
void powerDown500kPullDown();
void powerDown100kPullDown();
void powerDown1kPullDown();
uint16_t readValFromEEPROM();
uint16_t readCurrentDacVal();
private:
uint8_t _i2caddr;
};
23 changes: 23 additions & 0 deletions libraries/MCP4725/examples/waveform/waveform.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include "Arduino.h"
#include <Wire.h>
#include "MCP4725.h"
MCP4725 dac; //create a dac object

const PROGMEM uint16_t DACLookup_FullSine_6Bit[64] ={
2048, 2248, 2447, 2642, 2831, 3013, 3185, 3346, 3495, 3630, 3750, 3853, 3939, 4007, 4056, 4085, 4095, 4085, 4056, 4007, 3939, 3853, 3750, 3630,
3495, 3346, 3185, 3013, 2831, 2642, 2447, 2248, 2048, 1847, 1648, 1453, 1264, 1082, 910, 749, 600, 465, 345, 242, 156, 88, 39, 10,
0, 10, 39, 88, 156, 242, 345, 465, 600, 749, 910, 1082, 1264, 1453, 1648, 1847};

void setup(void) {
dac.begin(0x62); //addres for the dac is 0x62 (default) or 0x63 (A0 pin tied to VCC)
dac.setFastMode(); //comment this out to unset fastmode
}
void loop(void) {
uint16_t i;
for (i = 0; i < 64; i++){
//dac.setVoltage(pgm_read_word(&(DACLookup_FullSine_6Bit[i]))); //gives: 37Hz with FastMode unset; 110Hz with FastMode set.
dac.setVoltageFast(pgm_read_word(&(DACLookup_FullSine_6Bit[i]))); //gives: 48Hz with FastMode unset; 143Hz with FastMode set.
}
dac.powerDown500kPullDown();
delay(5);
}
Loading

0 comments on commit cc2caf3

Please sign in to comment.