Hackster is hosting Hackster Holidays, Ep. 5: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 5 on Friday!
Marco Zonca
Published © GPL3+

A Tiny Logic Probe TTL / CMOS with Battery Charger

Using an ATtiny1614 microcontroller with LiPo battery, including USB charging circuitry and logic. It is programmable with Arduino IDE!

AdvancedFull instructions provided3,103
A Tiny Logic Probe TTL / CMOS with Battery Charger

Things used in this project

Hardware components

Microchip ATtiny 1614 Processor
×1
Arduino Nano R3
Arduino Nano R3
×1

Story

Read more

Schematics

Main Fritzing schematic diagram

Microcontroller Fritzing schematic diagram

Microcontroller PCB overview

Main PCB overview

Main PCB bottom face

Main PCB top face

Main PCB components face

Microcontroller PCB bottom face

Microcontroller PCB top face

Microcontroller PCB components face

Code

LogicProbe code

Arduino
/*
  This sketch acts as a logic level tester, by Marco Zonca, 10/2020
  ATTINY 1614 as MPU, a button to switch TTL/CMOS (max 10v), 3 x LEDs and a few resistors, etc.;
 
  // ----------------------------------------------------------------------------------------------
  //  TTL levels : undefined=yellow >0.8v <= 2.0v  low=blue <= 0.8v  high=red > 2.0v                                     
  //  CMOS levels: undefined=yellow >1.5v <= 3.5v  low=blue <= 1.5v  high=red > 3.5v
  // ----------------------------------------------------------------------------------------------
    
  As a further step I added a small 300mA/h LiPo battery, code and circuitry to provide charging
  functionality: SMD micro relay, NPN BC337, 1N4148 diod and a few more resistors; for
  the two couples of 10k resistors as voltage dividers try to select them with similar values as possibile
  or you have to apply some +- % of arbitrary correction to the voltage calculation (i.e. VProbe=(n1...))
  for every one of them; it is another voltage divider, on probe input, 10k and 5k ohm values.

  Charging values: 
  -----------------s
  CBatt = 0.3 (battery power in A/h)
  VResis = 15.5 (resistor value in series between PowerSource and Battery, in ohm
               please measure the exact resistor value and put it here and below there)
  ICirc = 0.05 (estimated maximum current consumption of the circuit without battery charging)

  VPower = 5.0 (charging PowerSource voltage from USB) not a constant, it will change in reading
  VMinPower = 4.6 (minimum voltage from USB to charge battery)
  VBmin = 3.2 (absolute minimum voltage)
  VBWmin = 3.4 (working minimum voltage)
  VMax = 4.2 (absolute maximum voltage)
  VBchgd = 4.0 (battery voltage, or more, to consider it already charged at power-on)
  ETFact = 1.5 (estimated time of charging factor)
  OvTiFact = 1.2 (overtime of charging factor, in addition to ETFact)
  VInitBatt = initial battery voltage, after power-on of just after charged or at start of charging
  VBatt = actual battery voltage
  IBatt = (( VPower - VBatt ) / VResis) - ICirc (charging current; 0.066A at 0%, 0.034A at 50%, 0.002A at 100%)
  IBattAvg = (( VPower - ((VMax+VBmin)/2) ) / VResis) - ICirc = 0.034A (average charging current)
  SoC = 100 - (( VMax - VBatt ) * 100) (state of charge in %)
  TFull = ( CBatt / IBattAvg ) * 60 = 529 minutes (time of charging at average current and battery at VBmin)
  TMaxChg = TFull * ( VMax - VInitBatt ) * ETFact (estimated maximum time of charging considering initial voltage state, in minutes)
  TChg = ( TFull * (VMax - VBatt)) * ETFact  (restimated remaining time of charging in minutes)
  ChTimeStart = time elapsed as minutes from power-up
  ChTimeEnd = time end of charging as minute from power-up
  ChTimeNow = time as minutes from power-up
*/

// #include <SoftwareSerial.h>  // can be uncommented for isDEBUG=true and Serial Monitor

// ports in/out
const int ledBluePin = 0;  // pa4, on=LOW lovel (logic 0)
const int ledYellowPin = 6;  // pb1, on=UNDEFINED level in between 0 and 1
const int ledRedPin = 8;  // pa1, on=HIGH level (logic 1)
const int RelayPin = 2;  // pa6, set on (close) or off (open) the relay, due the contact is normally open
const int ProbePin = 10;  // pa3, analog input, logic level will be tested here
const int ButtonPin = 9;  // pa2, open/released=TTL closed/pressed=CMOS 
const int VBattPin = 7;  // pb0, analog input, battery voltage is tested here
const int VRawPin = 1;  // pa5, analog input, USB power source voltage is tested here

// Charging constant Values, see notes above
const float CBatt = 0.300;  // put the battery power A/h
const float VResis = 15.5;  // put the exact resistor value
const float ICirc = 0.05;  // put the maximum circuit consumption
const float VMinPower = 4.6;
const float VBmin = 3.2;
const float VBWmin = 3.4;
const float VMax = 4.2;
const float VBchgd = 4.0;
const float ETFact = 1.5;
const float OvTiFact = 1.2;
float VPower = 5.0;  // it will change with read value
float IBattAvg = ((VPower - ((VMax+VBmin)/2) ) / VResis) - ICirc;
float TFull = ( CBatt / IBattAvg ) * 60;

const boolean IsDEBUG = false;  // set this to 'true' for Serial Monitor

const int CLOSE = HIGH;
const int OPEN = LOW;
const float MillisReadVoltages = 2000;
const float MillisCheckCharge = 4000;
const float MillisDebug = 10000;

// Charging variable Values, see notes above
float VInitBatt = 0;
float VBatt = 0;
float IBatt = 0;
float SoC = 0;
float TMaxChg = 0;
float TChg = 0;
float VRaw = 0;
double ChTimeStart = 0;
double ChTimeEnd = 0;
double ChTimeNow = 0;
bool IsOverTime = false;
bool IsCharging = false;

float prevMillisReadVoltages = 0;
float prevMillisCheckCharge = 0;
float prevMillisDebug = 0;
bool isCMOS=false;
float n=0;
float n1=0;
float VProbe=0;
byte Status=0;  // 0=0 1=1 2=undefined
byte prevStatus=9;  // 0=0 1=1 2=undefined (previous) [value=9 is just for first loop run]

void setup() {
  if (IsDEBUG) Serial.begin(9600);
  pinMode(ledBluePin, OUTPUT);
  pinMode(ledYellowPin, OUTPUT);
  pinMode(ledRedPin, OUTPUT);
  pinMode(RelayPin, OUTPUT);
  pinMode(ButtonPin, INPUT);
  pinMode(ProbePin, INPUT);
  pinMode(VBattPin, INPUT);
  pinMode(VRawPin, INPUT);
  digitalWrite(RelayPin,OPEN);  // disconnects power from battery
  digitalWrite(ledBluePin,HIGH);  // LEDs test show
  delay(150);
  digitalWrite(ledBluePin,LOW);
  digitalWrite(ledYellowPin,HIGH);
  delay(150);
  digitalWrite(ledYellowPin,LOW);
  digitalWrite(ledRedPin,HIGH);
  delay(150);
  digitalWrite(ledRedPin,LOW);
}  // setup()

void loop() {
  readProbe();
  display();
  if ((prevMillisReadVoltages+MillisReadVoltages) < millis()) readVoltages();
  if ((prevMillisCheckCharge+MillisCheckCharge) < millis()) checkCharge();
  if (((prevMillisDebug+MillisDebug) < millis()) && (IsDEBUG)) printDebugging();
}  // loop()

void display() {
  digitalWrite(ledBluePin,LOW);
  digitalWrite(ledYellowPin,LOW);
  digitalWrite(ledRedPin,LOW);
  if (VPower > 0) {  // ------------------------- with USB power
    if (IsCharging==true) {
      if (IsOverTime==false) {
        if (SoC <= 20) blink_red();
        if (SoC > 20 && SoC <=40) blink_red_yellow();
        if (SoC > 40 && SoC <=60) blink_yellow();
        if (SoC > 60 && SoC <=80) blink_yellow_blue();
        if (SoC > 80 && SoC <=90) blink_blue();
        if (SoC > 90) blink_slowblue();
      } else {
        blink_quickyellow();  //overtime warning
      }
    } else {
      blink_quickblue();  //battery already charged
    }
  } else {
    switch (Status) {  // ----------------------- with battery, normal probing operation
      case 0:
        digitalWrite(ledBluePin, HIGH);
      break;
      case 1:
        digitalWrite(ledRedPin, HIGH);
      break;
      case 2:
        digitalWrite(ledYellowPin, HIGH);
      break;
    }
    prevStatus=Status;
  }
}  // display()


void readVoltages() {
  n=analogRead(VRawPin);
  if (n > 0) {
    n1=(((6.60 * n) / 1023.00));  // 6.60 = 3.30 x 2 (voltage divider /2 in input)
    VRaw=(n1 + ((n1 * 0.0) /100));  // arbitrary correction (not active, +-0.0%)
  } else {
    VRaw=0;
  }
  VPower=VRaw;
  n=analogRead(VBattPin);
  if (n > 0) {
    n1=(((6.60 * n) / 1023.00));  // 6.60 = 3.30 x 2 (voltage divider /2 in input)
    VBatt=(n1 + ((n1 * 0.0) /100));  // arbitrary correction (not active, +-0.0%)
  } else {
    VBatt=0;
  }
  if (VInitBatt==0) VInitBatt=VBatt;  // initial battery voltage, at power-on
  prevMillisReadVoltages=millis();
}  // readVoltages()


void checkCharge() {
  if ((IsCharging == false) && (VPower >= VMinPower) && (VBatt < VBchgd)) {
    ChTimeStart=int((millis()/1000)/60);  // start charging, elapsed time as minutes from power-up
    digitalWrite(RelayPin,CLOSE);  // connets power to battery
    VInitBatt=VBatt;  // initial charging battery voltage
    IsOverTime=false;
    IsCharging=true;
  }
  if ((IsCharging == true) && ((VPower < VMinPower) || (VBatt >= VMax))) {
    ChTimeEnd=int((millis()/1000)/60);  // end charging, elapsed time as minutes from power-up
    digitalWrite(RelayPin,OPEN);  // disconnects power from battery
    VInitBatt=VBatt;  // battery voltage, just after charging is finished
    IsCharging=false;
  }
  IBatt = ((VPower - VBatt) / VResis) - ICirc; // charging current; 0.066A at 0%, 0.034A at 50%, 0.002A at 100%
  IBattAvg = ((VPower - ((VMax+VBmin)/2) ) / VResis) - ICirc; // 0.034A average charging current
  SoC = 100 - ((VMax - VBatt) * 100); // state of charge in %
  TFull = (CBatt / IBattAvg) * 60;  // 529 minutes, time of charging at average current and battery at VBmin
  TMaxChg = TFull * (VMax - VInitBatt) * ETFact * OvTiFact; // estimated maximum time of charging considering initial voltage state, in minutes
  TChg = (TFull * (VMax - VBatt)) * ETFact;  //  estimated remaining time of charging in minutes
  ChTimeNow=int((millis()/1000)/60);  // now time as minutes from power-up
  if ((ChTimeNow-ChTimeStart) > TMaxChg) IsOverTime=true;
  prevMillisCheckCharge=millis();
}  // checkCharge()


void readProbe() {
  if (digitalRead(ButtonPin) == LOW) {
    isCMOS=true;
  } else {
    isCMOS=false;
  }
  n = analogRead(ProbePin);
  if (n > 0) {
    n1=(((9.90 * n) / 1023.00));  // 9.90 = 3.30 x 3 (voltage divider /3 in input)
    VProbe=(n1 + ((n1 * 0.0) /100));  // arbitrary correction (not active, +-0.0%)
  } else {
    VProbe=0;
  }
  if (isCMOS==false) {  // TTL case
    if (VProbe <= 0.8) {
      Status=0;
    }
    if (VProbe > 0.8 && VProbe <= 2.0) {
      Status=2;
    }
    if (VProbe > 2.0) {
      Status=1;
    }
  }
  if (isCMOS==true) {  // CMOS case
    if (VProbe <= 1.5) {
      Status=0;
    }
    if (VProbe > 1.5 && VProbe <= 3.5) {
      Status=2;
    }
    if (VProbe > 3.5) {
      Status=1;
    }
  }  
}  // readProbe()

void blink_red() {
  delay(500);
  digitalWrite(ledRedPin, HIGH);
}
void blink_blue() {
  delay(500);
  digitalWrite(ledBluePin, HIGH);
  delay(500);
}
void blink_yellow() {
  delay(500);
  digitalWrite(ledYellowPin, HIGH);
  delay(500);
}
void blink_red_yellow() {
  delay(500);
  digitalWrite(ledRedPin, HIGH);
  delay(500);
  digitalWrite(ledRedPin, LOW);
  digitalWrite(ledYellowPin, HIGH);
  delay(500);
}
void blink_yellow_blue() {
  delay(500);
  digitalWrite(ledYellowPin, HIGH);
  delay(500);
  digitalWrite(ledYellowPin, LOW);
  digitalWrite(ledBluePin, HIGH);
  delay(500);
}
void blink_slowblue() {
  delay(1000);
  digitalWrite(ledBluePin, HIGH);
  delay(2000);
}
void blink_quickyellow() {
  delay(100);
  digitalWrite(ledYellowPin, HIGH);
  delay(100);
}
void blink_quickblue() {
  delay(100);
  digitalWrite(ledBluePin, HIGH);
  delay(100);
}
void printDebugging() {
  Serial.println("----------------------------------------------");
  if (IsCharging) {
    Serial.print("IsCharging=");
    Serial.println(IsCharging);    
    Serial.print("VRaw (VPower)=");
    Serial.println(VRaw);
    Serial.print("VInitBatt=");
    Serial.println(VInitBatt);
    Serial.print("VBatt=");
    Serial.println(VBatt);
    Serial.print("IBatt=");
    Serial.println(IBatt); 
    Serial.print("IBattAvg=");
    Serial.println(IBattAvg); 
    Serial.print("SoC%=");
    Serial.println(SoC); 
    Serial.print("TFull=");
    Serial.println(TFull); 
    Serial.print("TMaxChg max minutes, more is OverTime=");
    Serial.println(TMaxChg); 
    Serial.print("TChg remaining minutes=");
    Serial.println(TChg); 
    Serial.print("ChTimeStart=");
    Serial.println(ChTimeStart);    
    Serial.print("TimeNow=");
    Serial.println(ChTimeNow);  
    Serial.print("IsOverTime=");
    Serial.println(IsOverTime);    
    Serial.print("VProbe=");
    Serial.println(VProbe); 
   } else {
    Serial.print("IsCharging=");
    Serial.println(IsCharging);    
    Serial.print("VRaw (VPower)=");
    Serial.println(VRaw);
    Serial.print("VInitBatt=");
    Serial.println(VInitBatt);
    Serial.print("VBatt=");
    Serial.println(VBatt);
    Serial.print("SoC%=");
    Serial.println(SoC); 
    Serial.print("ChTimeStart=");
    Serial.println(ChTimeStart);    
    Serial.print("ChTimeEnd=");
    Serial.println(ChTimeEnd);    
    Serial.print("TimeNow=");
    Serial.println(ChTimeNow);
    Serial.print("Millis()= ");
    Serial.println(millis());  
    Serial.print("VProbe=");
    Serial.println(VProbe); 
  }
  prevMillisDebug=millis();
}  // printDebugging()

Credits

Marco Zonca

Marco Zonca

12 projects • 43 followers
"From an early age I learned to not use pointers"

Comments