NikolaiRadke / Nokolino_diy

The small and naughty MP3 monster. DIY version.
http://www.monstermaker.de
Apache License 2.0
19 stars 5 forks source link

Attiny85/Digispark Support? #29

Open dnmtch opened 2 years ago

dnmtch commented 2 years ago

Hi (not a bug, feel free to close without notice)

I wanted to recreate a Nokilino with a Attiny85/Digispark. Unfortunately, I fail because of the used Libraries (EEPROM.h and SoftwareSerial.h). Am I doing something wrong or would the code have to be adapted for this? (DigisparkSoftSerial does not work for me)

NikolaiRadke commented 2 years ago

Hi!

I will have a look soon.

NikolaiRadke commented 2 years ago

Hello again,

i made a Digispark version. Check the src/ folder.

Hopefully it is working, i can't test it by myself, i don't have a Digispark module. Be sure to set the clock to 8 MHz or JQ6500 may have problems.

Good luck!

dnmtch commented 2 years ago

Thank you very much. I have here a "screaming" JQ8400-FL-10P which is connected to a Digispark. (Power via USB, all "eeprom_write_word" changed to EEPROM.write or EEPROM.read ).

Nokolino does not talk every minute. Code: https://github.com/NikolaiRadke/Nokolino_pcb/tree/master/src/Nokolino_V31 Code:


#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <SoftSerial.h>
#include <EEPROM.h>

//--------------------------------------------------------------------------------
// Configuation
#define Time         1             // Say something every statistical 10 minutes
#define Volume       25             // Volume 0-30 - 25 is recommended 
#define Darkness     4              // Optional: The lower the darker the light must be

#define Button_event 40             // Last button event number (XXX.mp3)
#define Time_event   163            // Last time event number -> Very last file is "beep"

//#define Breadboard                // Breadboard or PCB?
#ifdef Breadboard
  #define Offset       0.3          // Battery measuring error
  #define maxInput     50           // Max. value from busy line 
#else
  #define Offset       0.1
  #define maxInput     0
#endif

// Optional - comment out with // to disable o remove // to enable
#define StartupBeep                 // Will say "beep" when turned on
//#define BatteryWarning              // Gives a warning when battery is low
//#define LightSensor               // Will be quite in the dark
//#define SleepComplain             // Will complain if button pressed while its dark

//---------------------------------------------------------------------------------

// Optional battery warning
#define minCurrent   3.30 +Offset   // Low power warning current + measuring error
#define battLow      3.10 +Offset   // Minimal voltage before JQ8400 fails

// Hardware pins
#define TX      1
#define RX      2
#define Busy    2

// ADC and BOD
#ifndef cbi
  #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
  #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#define BODS 7                         // BOD sleep bit in MCUCR
#define BODSE 2                        // BOD sleep enable bit in MCUCR

// Variables
uint16_t seed,files;
uint16_t address=1;
volatile boolean f_wdt = 1;            // Volatile -> it is an interrupt routine
boolean  low=false;
boolean  dark=false;
char     count_files;
uint8_t  files_byte[6];

SoftSerial mp3(TX,RX);             // TX to D2, RX to D1

int main(void) {

#ifdef BatteryWarning
  uint16_t current;
  double vref;
  uint16_t counter=10;                 // Check shortly after startup
#endif

init(); {
  // Power saving
  MCUCR |= _BV(BODS) | _BV(BODSE);     // Disable brown out detection - default?
  ACSR |= _BV(ACD);                    // Disable analog comparator - default?
  setup_watchdog(3);                   // Set sleep time to 128ms  

  // Port Setup
  DDRB &= ~(1<<PB0);                   // D0 INPUT
  PORTB |= (1<<PB0);                   // D0 HIGH 

  // Start JQ8400
  newdelay(750);                       // JQ8400 needs a short time to settle
  mp3.begin(9600);
  mp3.write("\xAA\x13\x01");
  mp3.write(Volume);                   // Set volume 0-30
  mp3.write((uint8_t) 190+Volume);     // Calculate and write checksum
  newdelay(100);
  mp3.write("\xAA\x0C");               // Count files on module
  mp3.write((uint8_t) 0x00);
  mp3.write(0xB6);
  newdelay(100);
  for (seed=0;seed<6;seed++)           // Read 6 HEX chars from module
    files_byte[seed]=(uint8_t) mp3.read();// and convert the chars into uint8_t
  files=16*files_byte[3]+files_byte[4];// Convert 2 bytes into uint16_t

  // Nokolino mode | else Music box mode
  if (files==Time_event+1) {
    // Randomize number generator
    address=EEPROM.read(0);       // Read EEPROM address
    if ((address<2) || (address>(EEPROM.length()-3))) {           
    // Initialize EEPROM and size for first use or after end of cycle
      address=2;                       // Starting address
      EEPROM.write(0,address);    // Write starting address
      EEPROM.write(address,0);    // Write seed 0
    }
    seed=EEPROM.read(address);    // Read seed
    if (seed>900) {                    // After 900 write-cyles move to another address
      seed=0;                          // to keep the EEPROM alive
      address+=2;
      EEPROM.write(0,address);
    }
    randomSeed(seed);                  // Randomize
    seed++;                            // New seed
    EEPROM.write(address,seed);   // Save new seed for next startup    

    // Optional startup beep
    #ifdef StartupBeep    
      JQ8400_play(Time_event+1);       // Nokolino says "Beep"
      newdelay(1000);                  // Busy is not working well afer startup
    #endif 
  }   
  mp3.write("\xAA\x04");               // Stop and sleep        
  mp3.write((uint8_t) 0x00);           // Needed for Music box mode
  mp3.write(0xAE);                     // and no startup beep
  newdelay(100);
}

// Main loop
while(1) {
  // Wait for button or time and go to sleep - ~8 times/second         
  if (!low) {                          // Quiet if battery too low
    if (!(PINB & (1<<PB0))) {          // If button is pressed then
      if (dark) {                      // if fototransistor is available
        #ifdef SleepComplain           // and complain feature enabled
          if (files==Time_event+1)     // and not in music box mode
            JQ8400_play(Time_event);   // complain when button pressed
        #endif
      }
      else if (files==Time_event+1) 
        JQ8400_play(random(0,Button_event+1)); // Button event
      else {
        JQ8400_play(address);         // or single music box files 
        (address==files)? address=1:address++;
      }
    }
    else if ((!dark) && (files==Time_event+1) && (random(0,Time*60*8)==1)) // Time event
      JQ8400_play(random(Button_event+1,Time_event+1)); 
  }
  attiny_sleep();                      // Safe battery

  // Optional: Check current
  #ifdef BatteryWarning
    if (counter==0) {
      current=MeasureVCC();
      vref=1024*1.1f/(double)current;
      if (vref<=minCurrent) {          // Current below minimum
        if (vref<=battLow) low=true;   // Power too low for J8400
        else JQ8400_play(Time_event+1);// Nokolino says "Beep"
      }
      else low=false;
      counter=400;                      // Every minute, 400x128ms+some runtime ms for 60s
    }
    counter--;
  #endif

  // Optional: Check darkness
  #ifdef LightSensor
    if (analogRead(3)<=Darkness) dark=true;
    else dark=false;
  #endif
}}

void JQ8400_play(uint8_t f) {          // Plays MP3 file
  mp3.write("\xAA\x07\x02");           // Play file number f
  mp3.write((uint8_t) 0x00);
  mp3.write(f);
  mp3.write((uint8_t) 179+f);          // Calculate und wirte checksum
  newdelay(100);
  while (analogRead(2)>maxInput) 
    attiny_sleep();                    // Check busy
  newdelay(100);
 }

void attiny_sleep() {                  // Sleep to save power  
  cbi(ADCSRA,ADEN);                    // Switch ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); 
  sleep_mode();                                         
  sbi(ADCSRA,ADEN);                    // Switch ADC on
}

void newdelay(uint16_t z)              // New delay function to save flash
{
  uint32_t zmillis=millis();
  while (millis()-zmillis<z);
}

void setup_watchdog(uint8_t mode) {    // Setup wake time
  uint8_t bb;
  bb=mode & 7;
  if (mode > 7) bb|= (1<<5);
  bb|= (1<<WDCE);
  MCUSR &= ~(1<<WDRF);
  WDTCR |= (1<<WDCE) | (1<<WDE);
  WDTCR = bb;
  WDTCR |= _BV(WDIE);
}

uint16_t MeasureVCC(void) {           // Thank you, Tim!
  PRR    &=~_BV(PRADC); 
  ADCSRA  =_BV(ADEN)|_BV(ADPS2)|_BV(ADPS1)|_BV(ADPS0); 
  ADMUX   =_BV(REFS2) | 0x0c; 
  newdelay(1); 
  ADCSRA  |=_BV(ADSC);
  while (!(ADCSRA&_BV(ADIF))); 
  ADCSRA  |=_BV(ADIF);
  return ADC;
}

ISR(WDT_vect) {                       // Set global flag
  f_wdt=1; 
}
NikolaiRadke commented 2 years ago

I added a JQ8400 version here: https://github.com/NikolaiRadke/Nokolino_pcb/tree/master/src

Can you guess the time interval between the random talking? Maybe you should flash the bootloader to set the Attiny85 to 8 MHz.

dnmtch commented 2 years ago

The time intervals are mostly correct. The chip programmed with the correct settings (8mhz no USB). But the button does not work correctly with UBS Power. At most, no pressing is detected. With power from a battery, everything works as it should.

NikolaiRadke commented 2 years ago

I see. the hardware close programming to save flash space may cause problems with the digispark package. Now the button is questioned by standard Arduino code. Give it a try.