Digistump Forums

The Digispark => Digispark (Original) Support => Topic started by: GomezAddams on April 22, 2016, 09:56:47 am

Title: Yet another DIGI bricked! Pls help me to save the third one! [The DigiBricker]
Post by: GomezAddams on April 22, 2016, 09:56:47 am
Hi.

I need to put in wall switches box something to activate a fan every 10 seconds when the light is on. I could do with a relay, an LDR and a couple of timers, but instead I choose to use a digispark so it can count both the elapsed time and the number of transitions from dark to light.
I installed Arduino IDE and DIGI support on my laptop (win7). OK. I modified the Blink example and loaded to the AVR. OK. So I started to write down the sketch and to test it on the DIGI. Helas! after a few reloads, the DIGI suddenly stopped working.

Now I am the lucky owner of a DIGI that still blinks the LED to show it's alive, but that the IDE doesn't recognize anymore. Windows seven emits the 'Diing' sound when the DIGI is connected and the 'Doong' sound after some seconds when the bootloader calls my sketch.
Following some infos found on the NET I deleted everything, reinstalled the drivers and the IDE, used another USB port and a powered hub, I even installed the IDE on a desktop PC. The PC could non even load the driver (the DIGI was non present in the USB devices list).
So maybe the DIGI is partially dead.
As usual, I bought some more pieces than the strict necessary, so I have one DIGI for the development, another to install, and a third just in case something goes wrong.
I tested the IDE (1.58,1.67,1.68) with an Arduino Nano 328p. Everything OK. So I finished the sketch and debugged it using the Nano. The sketch runs ok, I only miss the LDR and use a POT instead.
At this point, I switch to the second DIGI. It works like a charm. It loads the modified blink and the DigiCDC sample, modified so it says 'TEST' AND also blinks. OK.
Back to my sketch. This time it loads (a little big, 99% of flash occupied) and runs. The serial output is a little garbled on the serial monitor. Maybe the speed is too high, so I change it to 4800 instead of 9600. I reload to the DIGI and ... Helas! upload went bad, please retry.
And from then on, it's impossible to upload anything. The IDE can't see the DIGI anymore.
BUT windows 7 can see it: when the DIGI is connected to a USB port I can hear the 'Diing' sound and I can see the DIGI on the devices list as virtual COM5, even if it doesn't blink or outputs something on the serial.

Now, before bricking the third (and last) DIGI, I should understand WHY my sketch is a DigiBricker!

I'm not enough into the insides of AVRs or the Digi to understand by myself, I could only blindly change my sketch and quite sure brick yet another DIGI.

So I have some questions:
- is it safe to load a sketch that use 99% of the flash?
- is it safe to use the Arduino EEPROM library (alone and with CDC and usertimer)?
- is it safe to use the usertimer? To tell the truth, I use the timer only for a programming habit, I could use the millis function instead.
- did I write something clearly WRONG for the DIGI even if it's ok for a nano?

In my opinion, the DigiBricker in some way corrupts the bootloader or interferes with it.

The external connections are very simple: a digital output to drive the relay (in my test only a LED + 1K resistor), a digital input to reset the counters for the current lamp, an analog input to read the POT/LDR. And the board mounted alive LED.
(No, I can't use the NANO instead of the DIGI: way too big!)

Here is the sketch.
*** WARNING ***  It's flawed, don't use it unless you know what you're doing or have a drawer full of DIGIs!

Thanks in advance for any help.
luca

Code: [Select]
/*
  LightControlledRelais

  created 15 Apr 2016
  by Luca Fornaciari

*/

#define __DEBUG 3   // 0=nessun debug  2=stampa lo stato ogni secondo  3=stampa informazioni iniziali

//#if defined( __AVR_ATtinyX5__ ) || defined( __AVR_ATtiny85__ )
#if defined ARDUINO_AVR_DIGISPARK
  #warning "ATTINY"
  #if __DEBUG > 2
    #undef __DEBUG
    #define __DEBUG 2
  #endif
  #include <usertimer.h>
  #include <C:\PROGRAM FILES (X86)\ARDUINO.1.68N\HARDWARE\ARDUINO\AVR\LIBRARIES\EEPROM\SRC\EEPROM.H>
  #if __DEBUG > 0
    #include <DigiCDC.h>
    #define Serial SerialUSB
    #define delay SerialUSB.delay
  #endif
  #define _TIMER_OVF_vect USERTIMER_OVF_vect
  #if TIMER_TO_USE_FOR_USER == 0
    #warning "USO IL TIMER 0"
    #define UserTimer_Prescale_Value  Timer0_Prescale_Value_1024
    #define _INTERRUPTS_PER_SECOND 64
  #elif TIMER_TO_USE_FOR_USER == 1
    #warning "USO IL TIMER 1" <=
    #define UserTimer_Prescale_Value  Timer1_Prescale_Value_16384
    #define _INTERRUPTS_PER_SECOND 4
  #else
    #error NESSUN TIMER UTENTE
  #endif
  //#define _TCNT TCNT(TIMER_TO_USE_FOR_USER)
  #define DIGITAL_LED 1
  #define ANALOG_INP  1    //Read P2
  #define DIGITAL_RELAIS  0 // Relais on P0
  //All pins are capable of digital input.
  #define DIGITAL_INP   5  //0 is P0, 1 is P1, 2 is P2, etc. - unlike the analog inputs, for digital inputs the pin number matches.
  //#define UserTimer_Prescale_Value_16384  UserTimer_(Prescale_Value_16384)
  #define _TIMER_COUNTER (256 - F_CPU/16384/_INTERRUPTS_PER_SECOND)  // preload timer
#else
  #warning "NOT  ATTINY"
  #include <EEPROM.h>
  #define _TIMER_OVF_vect TIMER1_OVF_vect
  #define _TCNT TCNT1
  #define DIGITAL_LED 13
  #define ANALOG_INP  0    //Read A0
  #define DIGITAL_RELAIS  2 // Relais on D2
  #define DIGITAL_INP   3 
  #define _INTERRUPTS_PER_SECOND 2
  #define _TIMER_COUNTER 34286  // preload timer 65536-16MHz/256/2Hz
#endif

// in un giorno ci sono 1440 minuti - 100.000 scritture durano 100000/1440=60 anni
//#define NUM_MIN_LOCATIONS 10  // numero di locations in EEPROM per allocare i minuti
//byte curTime_min[NUM_MIN_LOCATIONS] = {0,0,0,0,0,0,0,0,0,0};
//byte curTime_loc = 0; // quale locazione [0-NUM_MIN_LOCATIONS] sto usando
//byte curTime_sec = 0;
//byte curTime_min = 0;
//byte curTime_hr = 0;
//unsigned long curTime_day = 0L;

struct myTime {
  byte _sec;
  byte _min;
  byte _hr;
  unsigned long _day;
};
#define SOGLIASENSORE 225
#define DURATAPAUSARELE  10  // in secondi

//bool volatile OneSecond = false;
bool volatile OneTick = false;
myTime curTime = {50,58,23,0L}; // tempo trascorso dal caricamento
myTime onTime = {0,0,0,0L}; // tempo totale di luce
myTime lampTime = {0,0,0,0L}; // tempo dal cambio lampada
unsigned long currNumAccensioni = 0;
unsigned long lampNumAccensioni = 0;
#define eeBaseAddress 0
#define eeSignAddress eeBaseAddress
//const int eeSignAddress = eeBaseAddress;
#define eeCurAddress (eeBaseAddress + 1 * sizeof(unsigned long))
#define eeOnAddress (eeBaseAddress + 2 * sizeof(unsigned long))
#define eeLampAddress (eeBaseAddress + 3 * sizeof(unsigned long))
#define eeCurAccAddress (eeBaseAddress + 4 * sizeof(unsigned long))
#define eeLampAccAddress (eeBaseAddress + 5 * sizeof(unsigned long))

#define SIGNATURE 5 // increment to reset the counters in EEPROM


/*
* timer and interrupts
* Timer1 overflow interrupt example
* more infos: http://blog.oscarliang.net
*/


ISR(_TIMER_OVF_vect) // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
#if defined ARDUINO_AVR_DIGISPARK
  UserTimer_SetCount(_TIMER_COUNTER);
#else
  _TCNT = _TIMER_COUNTER; // preload timer
#endif
  //OneSecond = digitalRead(DIGITAL_LED);
  //digitalWrite(DIGITAL_LED, OneSecond ^ 1);
  //digitalWrite(DIGITAL_LED, digitalRead(DIGITAL_LED) ^ 1);
  OneTick = true;
}

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin DIGITAL_LED as an output.
  pinMode(DIGITAL_LED, OUTPUT);
  pinMode(DIGITAL_RELAIS, OUTPUT);
  pinMode(DIGITAL_INP, INPUT);
  digitalWrite(DIGITAL_INP,HIGH);   // to set the pullup resistor
  digitalWrite(DIGITAL_LED, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(200);              // wait for a second
  digitalWrite(DIGITAL_LED, LOW);    // turn the LED off by making the voltage LOW
  delay(200);              // wait for a second
  digitalWrite(DIGITAL_LED, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(100);              // wait for a second
  digitalWrite(DIGITAL_LED, LOW);    // turn the LED off by making the voltage LOW
  delay(100);              // wait for a second
  byte ActualSignature = 0;
  // EEPROM.get(eeSignAddress, ActualSignature);
  ActualSignature = EEPROM.read(eeSignAddress);
#if __DEBUG > 0
  #if defined( ARDUINO_AVR_DIGISPARK )
    Serial.begin();
  #else
    Serial.begin(9600);
    while (!Serial) {
      ; // wait for serial port to connect. Needed for native USB port only
    }
  #endif
#endif
#if __DEBUG > 2
  Serial.print("SIGNATURES: ");
  Serial.print(SIGNATURE);
  Serial.print("  ");
  Serial.println(ActualSignature);
#endif
  if (ActualSignature == SIGNATURE) {
    EEPROM.get(eeCurAddress, curTime._day);
    EEPROM.get(eeOnAddress, onTime._day);
    EEPROM.get(eeLampAddress, lampTime._day);
    EEPROM.get(eeCurAccAddress, currNumAccensioni);
    EEPROM.get(eeLampAccAddress, lampNumAccensioni);
 } else {
    EEPROM.update(eeSignAddress,SIGNATURE);
    EEPROM.put(eeCurAddress, curTime._day);
    EEPROM.put(eeOnAddress, onTime._day);
    EEPROM.put(eeLampAddress, lampTime._day);
    EEPROM.put(eeCurAccAddress, currNumAccensioni);
    EEPROM.put(eeLampAccAddress, lampNumAccensioni);
  }
#if __DEBUG > 2
  Serial.print(F("F_CPU = "));
  Serial.print(F_CPU,DEC);
  Serial.println(F(" Hz"));
  Serial.print(F("ARDUINO = "));
  Serial.print(ARDUINO);
  Serial.println(F(""));
#endif
  // initialize Timer1
  noInterrupts(); // disable all interrupts
#if defined( ARDUINO_AVR_DIGISPARK )
  UserTimer_SetToPowerup();
  UserTimer_SetCount(_TIMER_COUNTER);
  UserTimer_ClockSelect(UserTimer_Prescale_Value);    // il parametro è un enum 2^[0-15] 0=stop 1=CK/1 ... 15=CK/16384
  UserTimer_EnableOverflowInterrupt();
#else
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = _TIMER_COUNTER; // preload timer 65536-16MHz/256/2Hz
  TCCR1B |= (1 << CS12); // 256 prescaler
  TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt
#endif
  interrupts(); // enable all interrupts
}

bool UpdateTime(myTime *_t){
  bool ret = false;
  _t->_sec ++;
  if (_t->_sec >= 60) {
    _t->_sec = 0;
    _t->_min ++;
    if (_t->_min >= 60) {
      _t->_min = 0;
      _t->_hr ++;
      if (_t->_hr >= 24) {
        _t->_hr = 0;
        _t->_day ++;
        ret = true;
      }
    }
  }
#if __DEBUG > 1
  DumpTime(*_t);
#endif
  return ret;
}

#if __DEBUG > 1
void DumpTime(myTime _t){
  Serial.print(_t._day);
  Serial.print("  ");
  Serial.print(_t._hr);
  Serial.print(":");
  Serial.print(_t._min);
  Serial.print(":");
  Serial.print(_t._sec);
  Serial.print("\t");
}
#endif

// the loop function runs over and over again forever
bool ReleOn = false;
byte PausaRele = DURATAPAUSARELE;
byte tickCounter = 0;
//byte releCounter = 0;
bool luceEraAccesa = false;
void loop() {
  word valoreSensore = 0;
  bool luceAccesa = false;
  //if (OneSecond) {
  //  OneSecond = false;
  if (OneTick) {
    OneTick = false;
    tickCounter ++;
    if (tickCounter < _INTERRUPTS_PER_SECOND) {
      digitalWrite(DIGITAL_LED, 0);
    } else {
      tickCounter = 0;
      digitalWrite(DIGITAL_LED, 1);
      valoreSensore = analogRead(ANALOG_INP);
      luceAccesa = (valoreSensore >= SOGLIASENSORE);
      if (luceAccesa) {
        if (!luceEraAccesa) {
          currNumAccensioni++;
          lampNumAccensioni++;
        }
        if (ReleOn) {
          ReleOn = false;
          PausaRele = 0;
        } else {
          PausaRele++;
          if (PausaRele>=DURATAPAUSARELE) {
            ReleOn = true;
            //PausaRele = 0;
          }
        }
      }
      digitalWrite(DIGITAL_RELAIS, ReleOn);
#if __DEBUG > 1
      Serial.print(valoreSensore);
      Serial.print(" ");
      Serial.print(luceAccesa);
      Serial.print(" ");
      Serial.print(ReleOn);
      Serial.print("\t");
      Serial.print(currNumAccensioni);
      Serial.print(" ");
      Serial.print(lampNumAccensioni);
      Serial.print("\t\t");
#endif
      if (UpdateTime(&curTime)) {   // true->è passato un giorno
        EEPROM.put(eeCurAddress, curTime._day);
        EEPROM.put(eeCurAccAddress, currNumAccensioni);
        EEPROM.put(eeLampAccAddress, lampNumAccensioni);
        //Serial.print(F("\nUPDATE EEPROM  "));
    }
      if (luceAccesa) {
        if (UpdateTime(&onTime)) {
          EEPROM.put(eeOnAddress, onTime._day);
        }
        if (UpdateTime(&lampTime)) {
          EEPROM.put(eeLampAddress, lampTime._day);
        }
      } else {
        PausaRele = DURATAPAUSARELE;
      }
#if __DEBUG > 1
      Serial.println();
#endif
      luceEraAccesa = luceAccesa;
      if (!digitalRead(DIGITAL_INP)) {
        memset(&lampTime,0,sizeof(lampTime));
        lampNumAccensioni = 0;
      }
    }
  }
}