Sharp Proximity Sensor + ATTiny85 + IR emitter, help for Arduino and uC begginer

Hey all I am new to Arduino and MCU's in general. My initial attempt at posting was lost after about an hour of typing :sob: Also, apologies if this is in the wrong thread.

So this is going to be somewhat more concise and save you from a bit of the background I had before.

Just note the only formal programming learning I have had was over a decade ago as an elective in college, was C++, but don't remember any of it. Have dabbled in Python and BATCH over the years but just surface stuff.


Objectives
-ATtiny85 MCU device to power on and off TV based on proximity sensor readings.
-Continuously monitor for person/object (3 year old son too close to TV)
-If within X range for Y seconds power off TV (90cm for 6 seconds).
-Monitor now with intent to power on TV
-If nothing within X range for Y seconds power on TV.
-Repeat


Parts So far...
-Arduino Uno R3
-Sharp GP2Y0A60SZLF Analog Distance Sensor 10-150cm
-ATtiny85V-10PU
-OP293A IR Emitter
-Random Blue LED
-2x 220 ohm resistor
-1x 100uf cap

Unfortunately and kind of fortunately I was unable to find a DIY or Instructable on this type of thing so I've done more piecing from similar projects and other stuff on my own and have probably learned more than just following a step-by-step.


Schematic This is my first attempt at drawing one (don't be too judgmental :stuck_out_tongue: )


Code (For ATtiny85)

#include <IRremote.h>

unsigned int power[] = {4550, 4400, 600, 1650, 550, 1650, 600, 1650,
                        550, 550, 600, 500, 600, 550, 550, 550, 600, 500, 600, 1650, 600, 1600, 600,
                        1650, 550, 550, 600, 500, 600, 550, 600, 500, 600, 500, 650, 450, 650, 1600,
                        600, 500, 650, 450, 650, 500, 600, 500, 600, 500, 600, 550, 600, 1600, 600,
                        500, 650, 1600, 650, 1550, 650, 1600, 650, 1550, 650, 1600, 650, 1600, 600
                       };

IRsend irsend;
const int statusled = 0;   //Status led (ON when device turns TV OFF)
const int sensorpin = A1;  //PB7 -Sharp Sensor Input - 
int state = 1;
const long interval = 6000;  //6 seconds for monitoring proximity(s)



void setup() {


  pinMode(statusled, OUTPUT);

}

void loop() {
  int sensorValue = (analogRead(sensorpin));
  double distance = 187754 * pow(sensorValue, -1.51);  //distance value form proximity sensor (calculated to centimeters)

  unsigned long currentMillis = millis();  //value current time

  while (state == 1) {  //while loop to power TV OFF
    sensorValue = analogRead(sensorpin);           //
    distance = 187754 * pow(sensorValue, -1.51);   //getting new distance value
    unsigned long timer1 = millis();  //value for timer1
    if (timer1 - currentMillis >= interval) state = PowerTog(state, 1); //if timer reaches 6 secs call PowerTog - pass state and proximity breach
    if (state == 0) break;   //if TV state OFF leave while loop
    if (distance > 90) break; //if poximity above minimum leave loop

  }
  currentMillis = millis();

  while (state == 0) {     //while loop to power TV back ON
    sensorValue = analogRead(sensorpin);          //
    distance = 187754 * pow(sensorValue, -1.51);  //getting new distance value
    unsigned long timer2 = millis();  //value for timer2
    if (timer2 - currentMillis >= interval) state = PowerTog(state, 0); //if timer reaches 6 secs call PowerTog - pass state and proximity clear
    if (state == 1) break;   //if TV state ON leave while loop
    if (distance <= 90) break; //if poximity below minimum leave loop
  }

}


int PowerTog(int state, int prox) {
  if (prox == 1 && state == 1) { //proximity below minimum and TV state is ON
    irsend.sendRaw(power, 68, 38); // Power off TV
    digitalWrite(statusled, HIGH); //Sensor has powered off TV and is monitoring to power TV back ON.
    return 0; //TV state now OFF
  }

  if (prox == 0 && state == 0) { //proximity is above minimum and TV state is OFF
    irsend.sendRaw(power, 68, 38); //
    irsend.sendRaw(power, 68, 38); // Power on TV (Dont know why but requires 3 signals)
    irsend.sendRaw(power, 68, 38); //
    digitalWrite(statusled, LOW);  //Sensor back to initial state
    return 1; //TV state now ON
  }

  else return state;  //anything else return state

}

I am 100% sure there are better and more efficient ways to do the above, unfortunately I do not know those ways. It was hard not to use GOTO as it was, damn Batch( :stuck_out_tongue: , I'm such a begginer goto doesn't bug me, but read how it should only be used as a last resort etc etc).

If you would like to share, I am open to better ways but please provide good examples and/or ways to implement that doesn't break anything, or if so, how to fix it.


Library & Cores
https://github.com/z3t0/Arduino-IRremote
https://github.com/SpenceKonde/ATTinyCore
https://code.google.com/archive/p/arduino-tiny/ (Had to make new file boards.txt and copy and paste from Prospective Boards.txt)


A little background
I am using the Adruino as ISP ( initially IDE 1.6.8 ) method to burn bootloader and upload the sketch - had some setbacks at first but have this method down now.

Initially the ATtiny85 uploaded with basic sketches had no effect on the TV - don't know if it was the Core I was using (different from the ones posted) or the timing differences and delays. So I found some guides on tuning with TinyTuner using the Arduino as a USB to Serial adapter or an FTDI adapter - bought an FTDI (Prime is awesome - so fast). But neither worked - Could never get data on the Serial Monitor.

At the time my Core only had 8mhz internal or 16mhz external (did not know there was a 16mhz internal) - so thought I would have to do an external method - didn't know how to do this - googled a bit and found a crystal and calculated a pair of caps to go with it. As I waited on the crystals and caps ( not prime this time :frowning: ) did some more googling and found additional cores (the ones listed above) that gave me the 16mhz internal option, both would burn but only one would accept the sketch upload. I don't know if it was the core change or coding changes I had made since, but now it was semi working. (I think I had figured out ways to remove the delays in the code and switched to the Raw Data form of power code irsend -maybe some more stuff).

But my TV wouldn't power back on, the device would initiate and change states - my TV would a clicking noise as if it sensed something. Don't know why this worked or how I randomly chose to try this but sending the power code 3x when the TV is off made it possible to power it back on. Also, during this time for some reason, I downloaded IDE 1.6.5. Now both cores allow burning the bootloader and upload of the sketch.

And Finally...


The Problem
My device works as intended, almost... I just happened to notice this bug and don't know how to go about debugging it

The device as it works now:

-Monitors and turns TV off when someone is within 90cm for 6 seconds
-LED lights up
-Monitors if someone is still within 90cm - changes nothing

  • if nothing is detected for 6 seconds it powers TV On. LED turns off ***

***The bug is that the above is true only if I move out of line of sight. If I try to back away from it -within line of sight - past the 90cm mark it stays in the LED on stage (as if it still senses something within 90cm)

This is true even if I back up 9ft ( which is ~274cm). That is 184cm beyond what, I believe I coded it for, and 124cm beyond the specs of the max distance of the device

The only way I can think to debug the issue is to get the data from the analogRead and distance conversion and put it onto a serial monitor to see what the ATtiny is seeing, but have tried a few different guides on how to do this but each time I don't get anything


Hopefully the bug is a coding issue that someone here can see in a matter of moments, but I feel it has deeper roots.

Appreciate any help.

Thanks.

The only way I can think to debug the issue is to get the data from the analogRead and distance conversion and put it onto a serial monitor to see what the ATtiny is seeing, but have tried a few different guides on how to do this but each time I don't get anything

If you are using an Arduino compatible board to program...

http://www.ernstc.dk/arduino/tinycom.html

Start at Options 3-4 Using TinyISP. The default pin is MISO so you will have to use the "alternate pin" on the target (the ATtiny85) or move the IR emitter. I suggest just using the alternate pin.

Thanks for the reply. I'll give it ago later tonight after work.

I would probably use the "alternate pin", but would any of the pins I'm not currently using work. I purposely left physical pin 2 and 3 open for if I needed to use a crystal, and also the other guides I followed had pin 2 connecting to the Arduino.

I apologize. The published version does not have an "alternate pin".

In any case, this is the section of code that determines the default pin to use...

You can either modify that section, which is probably the easiest option, or define values for the KBS_* macros ...

Thanks for the heads up, I'll be off work in a couple hours - and sorry the stuff listed is a bit above my head, so how would I use the first method to assign physical pin 2 / PB3 ?

Do I only need to change:

#define MISO_PIN PINB

to

#define MISO_PIN PINB3

#if defined( __AVR_ATtiny13__ ) || defined( __AVR_ATtiny25__ ) || defined( __AVR_ATtiny45__ ) || defined( __AVR_ATtiny85__ )

  #define MISO_DDR    DDRB
  #define MISO_PORT   PORTB
  #define MISO_PIN    PINB
  #define MISO_BIT    3  /* <<<<<<<<<< */

  #define KNOCKBANG_SENDER_AVAILABLE 1

Thanks.
So I'm following the guide starting at: Options 3-4 Using TinyISP and forgive my ignorance but am getting no data or just symbols on the Serial Monitor. What pin does this need to connect to on the Arduino - I tried A0 and TX.


Steps I've taken.

  1. Downloaded TinyISP and put in my sketch folder as TinyISP

  2. Downloaded TinyDebugKnockBang put in my libraries folder

  3. Change the code in TinyDebugKnockBang.h as advised in the previous reply for PB3

  4. Was instructed to add "#define RELAY_KNOCK_BANG_ENABLED 1" but this was already there.
    I did notice that it instructed this for the "_TinyISP_BuildOption.h" tab, but I don't have this tab, I have "_TinyISP_BuildOptions.h" plural. Also my file looks a bit different than the one pictured.

  5. Uploaded the example sketch to my ATtiny85.

#include <TinyDebugKnockBang.h>

void setup( void )
{
Debug.begin( 250000 );
}

void loop( void )
{
Debug.println("Test knock-bang");
delay( 1000 );
}
  1. Uploaded the TinyISP to my Arduino

  2. Connected ATtiny85 -> Arduino via PB3 > 220ohm Resistor > A0/TX ?? (same result on both)

  3. Opened Serial Monitor chose 19200 baud, held reset button on Arduino and released a few seconds later, sent "!" - without quotes.


Results was either gibberish symbols or

--- Monitor starting ---

--- Monitor stopped ---


I tried 3 different Cores @ 8mhz internal

This core the Bootloader would burn but would not compile. Error:

TinyDebugKnockBang/TinyDebugKnockBang.h:172:28: error: '__FlashStringHelper' does not name a type static void print( const __FlashStringHelper * v ); )
  1. Connected ATtiny85 -> Arduino via PB3 > 220ohm Resistor > A0/TX ?? (same result on both)

MISO is the default on the Uno side; the "receive pin".

If this is set...

...pin 7 (PD7) is used instead.

--- Monitor starting ---

Is a good sign. TinyISP is installed and working.

Thanks so much, alternate pin 7 was set. Switching to it gave me the test prints immediately - now just need to apply it to my code when I get a little time in the next couple days.

@Coding Badly

Thanks for all your help- after looking at it with the KnockBang I still couldn't really grasp the issue, as the output looks ok.

I think there are sudden spikes in the analog voltage being sent to the ATtiny, which keep kicking out of the while loop to power the TV back on, thinking that it is detecting something below the minimum distance.

While this still doesn't explain the reason why it works when just moving out of line-of-sight vs backing away out of the sensor's range.

Though with this in mind I figured there should be some way to "average" out the readings and make it more stable. I found something about an RC-filter that I think I will want to add. But until then I have done some coding, that while the issue is still apparent, it seems to have mitigated it to a point that it is usable.

My code has changed quite a bit since the original, (started some c++ courses/tutorials - a little more knowledge and understanding) if there's anything anyone can think I can do to "tighten it up" please let me know.

void loop() {

  unsigned long currentMillis = millis();


  while (powerState == 1) {  //while loop to power TV OFF

    double centimeters = getDistance();
    if (centimeters > 60.96) break;
    if (millis() - currentMillis >= interval) powerState = powerTog(powerState, 1); //if timer reaches 6 secs call powerTog and change powerState

  }


  currentMillis = millis();

  while (powerState == 0) {  //while loop to power TV back ON

    double centimeters = getDistance();
    if (centimeters <= 60.96) break;
    if (millis() - currentMillis >= interval) powerState = powerTog(powerState, 0); //if timer reaches 6 secs call powerTog and change powerState
  }


}



// returns calculated distance in centimeters from analog input (mean)
double getDistance()
{
  return 187754 * pow(findMean(), -1.51);
}

double findMean()
{
  int size = 28;
  int arr1[size];
  double sum = 0.0;
  for (int i = 0; i < size; i++)
  {
    arr1[i] = analogRead(sensorPin);
  }

  combSort(arr1, size);

  for (int i = 0; i < size - 3; i++)    //add up array while ignoring the last 3 elements (Highest analogRead values / possible voltage spikes)
  {
    sum = sum + arr1[i];
  }

  return sum / (size - 3);  //return the average - of the now 25 values
}


//sorts array in ascending order
void combSort(int *ar, int n)
{
  int i, j, gap, swapped = 1;
  int temp;

  gap = n;
  while (gap > 1 || swapped == 1)
  {
    gap = gap * 10 / 13;
    if (gap == 9 || gap == 10) gap = 11;
    if (gap < 1) gap = 1;
    swapped = 0;
    for (i = 0, j = gap; j < n; i++, j++)
    {
      if (ar[i] > ar[j])
      {
        temp = ar[i];
        ar[i] = ar[j];
        ar[j] = temp;
        swapped = 1;
      }
    }
  }
}


int powerTog(int powerState, int proximity) {

  //proximity below minimum and TV state is ON
  if (proximity == 1 && powerState == 1) {
    irsend.sendRaw(power, 68, 38);    //power off TV
    digitalWrite(statusLED, HIGH);    //activate status LED
    return 0;                         //TV state now OFF
  }

  //proximity is above minimum and TV state is OFF
  if (proximity == 0 && powerState == 0) {
    irsend.sendRaw(power, 68, 38);
    irsend.sendRaw(power, 68, 38);     //power on TV (Dont know why but requires 3 signals)
    irsend.sendRaw(power, 68, 38);
    digitalWrite(statusLED, LOW);     //deactivate status LED
    return 1;                         //TV state now ON
  }

  else return powerState;  //any other combination (proximity/powerState) return original powerState

}

I was going to make these two statements into a function as well, but the couple of ways I thought to do it just made it a lot more "stuff" and to me a little more difficult to follow, than just the two lines.

if (millis() - currentMillis >= interval) powerState = powerTog(powerState, 1);
if (millis() - currentMillis >= interval) powerState = powerTog(powerState, 0);