Hey guys,
I need your help, because I can't find the root cause for this issue.
Following setup is intended:
- Attiny85 communcation via I2C
- Arduino Mega2560 is receiving data from the Attiny85
- An DS3231 is used (ZS-042) also communicating via I2C
- Working with Arduino IDE 1.8.19
- Attiny Core from Spence Konde
As soon as the whole circuit is done, an led stripe will be turned on and off based on the pre-defined alarms on the DS3231.
However, that's all future stuff, which should be handled by future Kerby...
Let's focus on the programming issue. I'm using the TinyI2C library from technoblogy (see TinyI2C Library), which can be also found in the code tags for readability reasons.
I created two additional files, called "Tiny_DS3231.cpp" and "Tiny_DS3231.h", which are containing some additional functions in order to separate them from the .ino file for readability reasons. One function reference can not be resolved and I don't know exactly why this is happening. The function concerning is called "send_debug (...)" and is originally defined under "Tiny_DS3231.cpp". Another function "get_currentTime (...)" is located in the same file, which for some reason can be successfully found by the linker. Hence, that doesn't make sense to me.
Following code is currently implemented:
I2C_ATTINY85.ino
#include <stdint.h>
#include <util/delay.h>
#include "Tiny_DS3231.h"
#include "TinyI2CMaster.h"
// ----------------------------------------------
// ATTINY85 PINOUT:
//
// -------
// RESET | V | VCC
// PIN3 | | PIN2 / SCL / INT0
// PIN4 | | PIN1
// GND | | PIN0 / SDA
// -------
//
// ----------------------------------------------
//#define DEBUG
#define TRS_PIN 3
#define LED_PIN 4
uint8_t mins, hrs, next_time;
uint8_t blink = 0;
uint8_t time[2];
uint8_t led = 0;
void setup()
{
// Calibrates the internal register value for the timer accuracy:
OSCCAL -= 3;
TinyI2C.init();
pinMode(LED_PIN, OUTPUT);
pinMode(TRS_PIN, OUTPUT);
get_currentTime(time);
mins = time[0];
hrs = time[1];
// Every 5th multiple of minutes:
next_time = ((mins + 4) / 5) *5;
}
void loop()
{
get_currentTime(time);
digitalWrite(LED_PIN, (blink = ~blink));
//#ifdef DEBUG
send_debug(time, 2);
//#endif
if (!(mins % 5) && (mins == next_time))
{
digitalWrite(TRS_PIN, (led = ~led));
next_time = mins + 5;
if (next_time >= 60)
{
next_time = 0;
}
}
_delay_ms(1000);
}
Tiny_DS3231.cpp
// ******************************************************
//
// Framework for accessing DS3231 registers with the
// Attiny85 via I2C based on the library "TinyI2C"
// from technoblogy
//
// ******************************************************
#include "Tiny_DS3231.h"
#include "TinyI2CMaster.h"
void send_debug(void *data_, uint32_t datalen)
{
uint8_t* data = (uint8_t*) data_;
TinyI2C.start(MEGA2560_ADDRESS, 0);
for (uint32_t i = 0; i < datalen; ++i)
{
TinyI2C.write(*(data+i));
}
TinyI2C.stop();
}
void get_currentTime(uint8_t *time)
{
// Read the time from the RTC:
TinyI2C.start(DS3231_ADDRESS, 0);
{
// Start reading at register for minutes:
TinyI2C.write(0x01);
}
TinyI2C.restart(DS3231_ADDRESS, 2);
{
time[0] = TinyI2C.read(); // Register 0x01: Minutes
time[1] = TinyI2C.read(); // Register 0x02: Hours
}
TinyI2C.stop();
}
Tiny_DS3231.h
// ******************************************************
//
// Framework for accessing DS3231 registers
// with the Attiny85 via I2C
//
// ******************************************************
#ifndef _TINY_DS3231_H_
#define _TINY_DS3231_H_
#include <stdint.h>
#define DS3231_ADDRESS (0x68)
#define MEGA2560_ADDRESS (0x01)
void get_currentTime(uint8_t *time);
void send_debug(void *data, int datalen);
#endif // _TINY_DS3231_H_
TinyI2CMaster.cpp
/* Tiny I2C
David Johnson-Davies - www.technoblogy.com - 14th April 2018
CC BY 4.0
Licensed under a Creative Commons Attribution 4.0 International license:
http://creativecommons.org/licenses/by/4.0/
*/
#include "TinyI2CMaster.h"
TinyI2CMaster::TinyI2CMaster()
{
}
// Minimal Tiny I2C Routines **********************************************
uint8_t TinyI2CMaster::transfer (uint8_t data) {
USISR = data; // Set USISR according to data.
// Prepare clocking.
data = 0<<USISIE | 0<<USIOIE | // Interrupts disabled
1<<USIWM1 | 0<<USIWM0 | // Set USI in Two-wire mode.
1<<USICS1 | 0<<USICS0 | 1<<USICLK | // Software clock strobe as source.
1<<USITC; // Toggle Clock Port.
do {
DELAY_T2TWI;
USICR = data; // Generate positive SCL edge.
while (!(PIN_USI_CL & 1<<PIN_USI_SCL)); // Wait for SCL to go high.
DELAY_T4TWI;
USICR = data; // Generate negative SCL edge.
} while (!(USISR & 1<<USIOIF)); // Check for transfer complete.
DELAY_T2TWI;
data = USIDR; // Read out data.
USIDR = 0xFF; // Release SDA.
DDR_USI |= (1<<PIN_USI_SDA); // Enable SDA as output.
return data; // Return the data from the USIDR
}
void TinyI2CMaster::init () {
PORT_USI |= 1<<PIN_USI_SDA; // Enable pullup on SDA.
PORT_USI_CL |= 1<<PIN_USI_SCL; // Enable pullup on SCL.
DDR_USI_CL |= 1<<PIN_USI_SCL; // Enable SCL as output.
DDR_USI |= 1<<PIN_USI_SDA; // Enable SDA as output.
USIDR = 0xFF; // Preload data register with "released level" data.
USICR = 0<<USISIE | 0<<USIOIE | // Disable Interrupts.
1<<USIWM1 | 0<<USIWM0 | // Set USI in Two-wire mode.
1<<USICS1 | 0<<USICS0 | 1<<USICLK | // Software stobe as counter clock source
0<<USITC;
USISR = 1<<USISIF | 1<<USIOIF | 1<<USIPF | 1<<USIDC | // Clear flags,
0x0<<USICNT0; // and reset counter.
}
uint8_t TinyI2CMaster::read (void) {
if ((I2Ccount != 0) && (I2Ccount != -1)) I2Ccount--;
/* Read a byte */
DDR_USI &= ~(1<<PIN_USI_SDA); // Enable SDA as input.
uint8_t data = TinyI2CMaster::transfer(USISR_8bit);
/* Prepare to generate ACK (or NACK in case of End Of Transmission) */
if (I2Ccount == 0) USIDR = 0xFF; else USIDR = 0x00;
TinyI2CMaster::transfer(USISR_1bit); // Generate ACK/NACK.
return data; // Read successfully completed
}
uint8_t TinyI2CMaster::readLast (void) {
I2Ccount = 0;
return TinyI2CMaster::read();
}
bool TinyI2CMaster::write (uint8_t data) {
/* Write a byte */
PORT_USI_CL &= ~(1<<PIN_USI_SCL); // Pull SCL LOW.
USIDR = data; // Setup data.
TinyI2CMaster::transfer(USISR_8bit); // Send 8 bits on bus.
/* Clock and verify (N)ACK from slave */
DDR_USI &= ~(1<<PIN_USI_SDA); // Enable SDA as input.
if (TinyI2CMaster::transfer(USISR_1bit) & 1<<TWI_NACK_BIT) return false;
return true; // Write successfully completed
}
// Start transmission by sending address
bool TinyI2CMaster::start (uint8_t address, int readcount) {
if (readcount != 0) { I2Ccount = readcount; readcount = 1; }
uint8_t addressRW = address<<1 | readcount;
/* Release SCL to ensure that (repeated) Start can be performed */
PORT_USI_CL |= 1<<PIN_USI_SCL; // Release SCL.
while (!(PIN_USI_CL & 1<<PIN_USI_SCL)); // Verify that SCL becomes high.
#ifdef TWI_FAST_MODE
DELAY_T4TWI;
#else
DELAY_T2TWI;
#endif
/* Generate Start Condition */
PORT_USI &= ~(1<<PIN_USI_SDA); // Force SDA LOW.
DELAY_T4TWI;
PORT_USI_CL &= ~(1<<PIN_USI_SCL); // Pull SCL LOW.
PORT_USI |= 1<<PIN_USI_SDA; // Release SDA.
if (!(USISR & 1<<USISIF)) return false;
/*Write address */
PORT_USI_CL &= ~(1<<PIN_USI_SCL); // Pull SCL LOW.
USIDR = addressRW; // Setup data.
TinyI2CMaster::transfer(USISR_8bit); // Send 8 bits on bus.
/* Clock and verify (N)ACK from slave */
DDR_USI &= ~(1<<PIN_USI_SDA); // Enable SDA as input.
if (TinyI2CMaster::transfer(USISR_1bit) & 1<<TWI_NACK_BIT) return false; // No ACK
return true; // Start successfully completed
}
bool TinyI2CMaster::restart(uint8_t address, int readcount) {
return TinyI2CMaster::start(address, readcount);
}
void TinyI2CMaster::stop (void) {
PORT_USI &= ~(1<<PIN_USI_SDA); // Pull SDA low.
PORT_USI_CL |= 1<<PIN_USI_SCL; // Release SCL.
while (!(PIN_USI_CL & 1<<PIN_USI_SCL)); // Wait for SCL to go high.
DELAY_T4TWI;
PORT_USI |= 1<<PIN_USI_SDA; // Release SDA.
DELAY_T2TWI;
}
TinyI2CMaster TinyI2C = TinyI2CMaster(); // Instantiate a TinyI2C object
TinyI2CMaster.h
/* Tiny I2C
David Johnson-Davies - www.technoblogy.com - 28th May 2019
CC BY 4.0
Licensed under a Creative Commons Attribution 4.0 International license:
http://creativecommons.org/licenses/by/4.0/
*/
#ifndef TinyI2CMaster_h
#define TinyI2CMaster_h
#include <stdint.h>
#include <Arduino.h>
#include <avr/io.h>
#include <util/delay.h>
// Defines
#define TWI_FAST_MODE
#ifdef TWI_FAST_MODE // TWI FAST mode timing limits. SCL = 100-400kHz
#define DELAY_T2TWI (_delay_us(2)) // >1.3us
#define DELAY_T4TWI (_delay_us(1)) // >0.6us
#else // TWI STANDARD mode timing limits. SCL <= 100kHz
#define DELAY_T2TWI (_delay_us(5)) // >4.7us
#define DELAY_T4TWI (_delay_us(4)) // >4.0us
#endif
#define TWI_NACK_BIT 0 // Bit position for (N)ACK bit.
// Constants
// Prepare register value to: Clear flags, and set USI to shift 8 bits i.e. count 16 clock edges.
const unsigned char USISR_8bit = 1<<USISIF | 1<<USIOIF | 1<<USIPF | 1<<USIDC | 0x0<<USICNT0;
// Prepare register value to: Clear flags, and set USI to shift 1 bit i.e. count 2 clock edges.
const unsigned char USISR_1bit = 1<<USISIF | 1<<USIOIF | 1<<USIPF | 1<<USIDC | 0xE<<USICNT0;
class TinyI2CMaster {
public:
TinyI2CMaster();
void init(void);
uint8_t read(void);
uint8_t readLast(void);
bool write(uint8_t data);
bool start(uint8_t address, int readcount);
bool restart(uint8_t address, int readcount);
void stop(void);
private:
int I2Ccount;
uint8_t transfer(uint8_t data);
};
extern TinyI2CMaster TinyI2C;
#endif
The error from the Arduino IDE is the following, when trying to compile the .ino:
Arduino: 1.8.19 (Windows Store 1.8.57.0) (Windows 10), Board: "ATtiny25/45/85 (No bootloader), Enabled, CPU (CPU frequency), ATtiny85, 8 MHz (internal), EEPROM retained, B.O.D. Disabled (saves power), Enabled"
C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.57.0_x86__mdqgnx93n4wtt\arduino-builder -dump-prefs -logger=machine -hardware C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.57.0_x86__mdqgnx93n4wtt\hardware -hardware C:\Users\<User>\Documents\ArduinoData\packages -tools C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.57.0_x86__mdqgnx93n4wtt\tools-builder -tools C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.57.0_x86__mdqgnx93n4wtt\hardware\tools\avr -tools C:\Users\<User>\Documents\ArduinoData\packages -built-in-libraries C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.57.0_x86__mdqgnx93n4wtt\libraries -libraries C:\Users\<User>\Documents\Arduino\libraries -fqbn=ATTinyCore:avr:attinyx5:LTO=enable,TimerClockSource=default,chip=85,clock=8internal,eesave=aenable,bod=disable,millis=enabled -vid-pid=2341_0043 -ide-version=10819 -build-path C:\Users\<User>\AppData\Local\Temp\arduino_build_67524 -warnings=all -build-cache C:\Users\<User>\AppData\Local\Temp\arduino_cache_712885 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.avr-gcc.path=C:\Users\<User>\Documents\ArduinoData\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7 -prefs=runtime.tools.avr-gcc-7.3.0-atmel3.6.1-arduino7.path=C:\Users\<User>\Documents\ArduinoData\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7 -prefs=runtime.tools.micronucleus.path=C:\Users\<User>\Documents\ArduinoData\packages\ATTinyCore\tools\micronucleus\2.5-azd1b -prefs=runtime.tools.micronucleus-2.5-azd1b.path=C:\Users\<User>\Documents\ArduinoData\packages\ATTinyCore\tools\micronucleus\2.5-azd1b -prefs=runtime.tools.avrdude.path=C:\Users\<User>\Documents\ArduinoData\packages\arduino\tools\avrdude\6.3.0-arduino18 -prefs=runtime.tools.avrdude-6.3.0-arduino18.path=C:\Users\<User>\Documents\ArduinoData\packages\arduino\tools\avrdude\6.3.0-arduino18 -verbose C:\Users\<User>\Documents\Arduino\I2C_ATTINY85\I2C_ATTINY85.ino
C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.57.0_x86__mdqgnx93n4wtt\arduino-builder -compile -logger=machine -hardware C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.57.0_x86__mdqgnx93n4wtt\hardware -hardware C:\Users\<User>\Documents\ArduinoData\packages -tools C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.57.0_x86__mdqgnx93n4wtt\tools-builder -tools C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.57.0_x86__mdqgnx93n4wtt\hardware\tools\avr -tools C:\Users\<User>\Documents\ArduinoData\packages -built-in-libraries C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.57.0_x86__mdqgnx93n4wtt\libraries -libraries C:\Users\<User>\Documents\Arduino\libraries -fqbn=ATTinyCore:avr:attinyx5:LTO=enable,TimerClockSource=default,chip=85,clock=8internal,eesave=aenable,bod=disable,millis=enabled -vid-pid=2341_0043 -ide-version=10819 -build-path C:\Users\<User>\AppData\Local\Temp\arduino_build_67524 -warnings=all -build-cache C:\Users\<User>\AppData\Local\Temp\arduino_cache_712885 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.avr-gcc.path=C:\Users\<User>\Documents\ArduinoData\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7 -prefs=runtime.tools.avr-gcc-7.3.0-atmel3.6.1-arduino7.path=C:\Users\<User>\Documents\ArduinoData\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7 -prefs=runtime.tools.micronucleus.path=C:\Users\<User>\Documents\ArduinoData\packages\ATTinyCore\tools\micronucleus\2.5-azd1b -prefs=runtime.tools.micronucleus-2.5-azd1b.path=C:\Users\<User>\Documents\ArduinoData\packages\ATTinyCore\tools\micronucleus\2.5-azd1b -prefs=runtime.tools.avrdude.path=C:\Users\<User>\Documents\ArduinoData\packages\arduino\tools\avrdude\6.3.0-arduino18 -prefs=runtime.tools.avrdude-6.3.0-arduino18.path=C:\Users\<User>\Documents\ArduinoData\packages\arduino\tools\avrdude\6.3.0-arduino18 -verbose C:\Users\<User>\Documents\Arduino\I2C_ATTINY85\I2C_ATTINY85.ino
Using board 'attinyx5' from platform in folder: C:\Users\<User>\Documents\ArduinoData\packages\ATTinyCore\hardware\avr\1.5.2
Using core 'tiny' from platform in folder: C:\Users\<User>\Documents\ArduinoData\packages\ATTinyCore\hardware\avr\1.5.2
Detecting libraries used...
"C:\\Users\\<User>\\Documents\\ArduinoData\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino7/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=attiny85 -DF_CPU=8000000L -DCLOCK_SOURCE=0 -DARDUINO=10819 -DARDUINO_AVR_ATTINYX5 -DARDUINO_ARCH_AVR -DNEOPIXELPORT=PORTB "-IC:\\Users\\<User>\\Documents\\ArduinoData\\packages\\ATTinyCore\\hardware\\avr\\1.5.2\\cores\\tiny" "-IC:\\Users\\<User>\\Documents\\ArduinoData\\packages\\ATTinyCore\\hardware\\avr\\1.5.2\\variants\\tinyX5" "C:\\Users\\<User>\\AppData\\Local\\Temp\\arduino_build_67524\\sketch\\I2C_ATTINY85.ino.cpp" -o nul
Using cached library dependencies for file: C:\Users\<User>\AppData\Local\Temp\arduino_build_67524\sketch\TinyI2CMaster.cpp
Using cached library dependencies for file: C:\Users\<User>\AppData\Local\Temp\arduino_build_67524\sketch\Tiny_DS3231.cpp
Generating function prototypes...
"C:\\Users\\<User>\\Documents\\ArduinoData\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino7/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=attiny85 -DF_CPU=8000000L -DCLOCK_SOURCE=0 -DARDUINO=10819 -DARDUINO_AVR_ATTINYX5 -DARDUINO_ARCH_AVR -DNEOPIXELPORT=PORTB "-IC:\\Users\\<User>\\Documents\\ArduinoData\\packages\\ATTinyCore\\hardware\\avr\\1.5.2\\cores\\tiny" "-IC:\\Users\\<User>\\Documents\\ArduinoData\\packages\\ATTinyCore\\hardware\\avr\\1.5.2\\variants\\tinyX5" "C:\\Users\\<User>\\AppData\\Local\\Temp\\arduino_build_67524\\sketch\\I2C_ATTINY85.ino.cpp" -o "C:\\Users\\<User>\\AppData\\Local\\Temp\\arduino_build_67524\\preproc\\ctags_target_for_gcc_minus_e.cpp"
"C:\\Program Files\\WindowsApps\\ArduinoLLC.ArduinoIDE_1.8.57.0_x86__mdqgnx93n4wtt\\tools-builder\\ctags\\5.8-arduino11/ctags" -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives "C:\\Users\\<User>\\AppData\\Local\\Temp\\arduino_build_67524\\preproc\\ctags_target_for_gcc_minus_e.cpp"
Compiling sketch...
"C:\\Users\\<User>\\Documents\\ArduinoData\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino7/bin/avr-g++" -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -mmcu=attiny85 -DF_CPU=8000000L -DCLOCK_SOURCE=0 -DARDUINO=10819 -DARDUINO_AVR_ATTINYX5 -DARDUINO_ARCH_AVR -DNEOPIXELPORT=PORTB "-IC:\\Users\\<User>\\Documents\\ArduinoData\\packages\\ATTinyCore\\hardware\\avr\\1.5.2\\cores\\tiny" "-IC:\\Users\\<User>\\Documents\\ArduinoData\\packages\\ATTinyCore\\hardware\\avr\\1.5.2\\variants\\tinyX5" "C:\\Users\\<User>\\AppData\\Local\\Temp\\arduino_build_67524\\sketch\\I2C_ATTINY85.ino.cpp" -o "C:\\Users\\<User>\\AppData\\Local\\Temp\\arduino_build_67524\\sketch\\I2C_ATTINY85.ino.cpp.o"
Using previously compiled file: C:\Users\<User>\AppData\Local\Temp\arduino_build_67524\sketch\Tiny_DS3231.cpp.o
Using previously compiled file: C:\Users\<User>\AppData\Local\Temp\arduino_build_67524\sketch\TinyI2CMaster.cpp.o
Compiling libraries...
Compiling core...
Using precompiled core: C:\Users\<User>\AppData\Local\Temp\arduino_cache_712885\core\core_9558a1c025a50e12ab415d105f7eef9f.a
Linking everything together...
"C:\\Users\\<User>\\Documents\\ArduinoData\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino7/bin/avr-gcc" -Wall -Wextra -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections -mmcu=attiny85 -o "C:\\Users\\<User>\\AppData\\Local\\Temp\\arduino_build_67524/I2C_ATTINY85.ino.elf" "C:\\Users\\<User>\\AppData\\Local\\Temp\\arduino_build_67524\\sketch\\I2C_ATTINY85.ino.cpp.o" "C:\\Users\\<User>\\AppData\\Local\\Temp\\arduino_build_67524\\sketch\\TinyI2CMaster.cpp.o" "C:\\Users\\<User>\\AppData\\Local\\Temp\\arduino_build_67524\\sketch\\Tiny_DS3231.cpp.o" "C:\\Users\\<User>\\AppData\\Local\\Temp\\arduino_build_67524/..\\arduino_cache_712885\\core\\core_9558a1c025a50e12ab415d105f7eef9f.a" "-LC:\\Users\\<User>\\AppData\\Local\\Temp\\arduino_build_67524" -lm
C:\Users\<User>\AppData\Local\Temp\ccqfSMek.ltrans0.ltrans.o: In function `loop':
C:\Users\<User>\Documents\Arduino\I2C_ATTINY85/I2C_ATTINY85.ino:56: undefined reference to `send_debug(void*, int)'
collect2.exe: error: ld returned 1 exit status
exit status 1
Error compiling for board ATtiny25/45/85 (No bootloader).
Do you guys have any idea how to solve it? I probably tried everything.
Tried to move it to the libraries folder and accessing it via
#include <Tiny_DS3231.h>
hasn't solved the issue.
I also changed the name of the function from "send_debug" to something like "check_var", which hasn't solved the issue, too.
All files are located in one folder. No other folders are existing in that one. The Arduino IDE detects all files fine, which is visible by the top tabs (exactly 5 tabs for 5 files).
Remark: If you are checking the paths, I manually replaced my user name with "<User>".
Thanks for any helpful advice!
Kerby