I've been working on a project for the last few days and spent a LOT of time googling, reading and testing. I'm kinda running out of ideas now and instead of bumping my head to the wall I thought posting my issue here in the hopes someone might be able to point me towards the right direction
I've got an Arduino UNO programmed as I2C Master and an ATtiny85 as an I2C slave. An single Adafruit Neopixel LED is attached to the slave.
When running the two sketches below everything actually works perfect.....for about a minute or so. While during correct operations the Neopixel LED is actually changing color correctly and the serial monitor on the Uno shows the correctly updated R-G-B values, things change after about 1 minute of running. The LED keeps blinking, which tells me the ATtiny is not frozen. The UNO keeps sending I2C and is receiving I2C data...but the data is constant. The updated RGB that the Uno (master) has been sending to the ATtiny slave are somehow not handled (correctly) by the slave.
Any thoughts are more then appreciated. I'm totally lost and do not know which directions to go from here so any pointers would be more then welcome.
The Master - Arduino Uno -
#include <Wire.h>
#define I2C_MASTER_ADDR 0x04
#define I2C_SLAVE_ADDR 0x05
/*
* Software config
*/
int pollInterval = 700;//Milliseconds
//Demo's
#define demoLedInterval 150//In milliseconds
boolean demoRequest = false;
/*
* Internal variables
*/
unsigned long lastPoll = 0;
int demoLedState = 0;
unsigned long lastDemoLedUpdate = 0;
/*
* Setup function
*/
void setup()
{
Wire.begin(I2C_MASTER_ADDR); // join i2c bus (address optional for master)
Serial.begin(115200); // start serial for output
Serial.println("Setup complete");
}
/*
* The main loop
*/
int i =0;
void loop()
{
/*
* Poll the slave
*/
if( (millis()-lastPoll) > pollInterval)
{
/*
*
*/
int red=0; int green=0; int blue=0;
if(i==0)
{
Serial.println("Write data to slave - set led red");
Wire.beginTransmission(I2C_SLAVE_ADDR);
Wire.write(0xC2);//Command
Wire.write(0x00);
Wire.write(255);//Data
Wire.endTransmission();
Wire.beginTransmission(I2C_SLAVE_ADDR);
Wire.write(0xC3);//Command
Wire.write(0x00);
Wire.write(0);//Data
Wire.endTransmission();
Wire.beginTransmission(I2C_SLAVE_ADDR);
Wire.write(0xC4);//Command
Wire.write(0x00);
Wire.write(0);//Data
Wire.endTransmission();
}
if(i==1)
{
Serial.println("Write data to slave - set led green");
Wire.beginTransmission(I2C_SLAVE_ADDR);
Wire.write(0xC2);//Command
Wire.write(0x00);
Wire.write(0);//Data
Wire.endTransmission();
Wire.beginTransmission(I2C_SLAVE_ADDR);
Wire.write(0xC3);//Command
Wire.write(0x00);
Wire.write(255);//Data
Wire.endTransmission();
Wire.beginTransmission(I2C_SLAVE_ADDR);
Wire.write(0xC4);//Command
Wire.write(0x00);
Wire.write(0);//Data
Wire.endTransmission();
}
if(i==2)
{
Serial.println("Write data to slave - set led blue");
Wire.beginTransmission(I2C_SLAVE_ADDR);
Wire.write(0xC2);//Command
Wire.write(0x00);
Wire.write(0);//Data
Wire.endTransmission();
Wire.beginTransmission(I2C_SLAVE_ADDR);
Wire.write(0xC3);//Command
Wire.write(0x00);
Wire.write(0);//Data
Wire.endTransmission();
Wire.beginTransmission(I2C_SLAVE_ADDR);
Wire.write(0xC4);//Command
Wire.write(0x00);
Wire.write(255);//Data
Wire.endTransmission();
}
i++; if(i>2) i =0;
/*
* General request (without data). Should return something though
*/
Serial.println("General request for slave");
Wire.requestFrom(I2C_SLAVE_ADDR, 7);//Request N bytes, N=byte_len
delay(10);//Give the slave time to answer
while (Wire.available())
{
uint8_t next_byte = Wire.read();
Serial.print(next_byte);Serial.print(" ");
}
Serial.println("\n");
lastPoll = millis();
}//End if time to poll again
}//End loop
The Slave code - ATtiny 85 - Main file
#include <EEPROM.h>
#include <OneWire.h>
#include <TinyWireS.h>
#include <Bounce2.h>
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__ //Which will be true for ATtiny85
#include <avr/power.h>
#endif
#define I2C_SLAVE_DEFAULT_ADDR 0x05
#include "namespaces.h"//Include at last to make sure all libs are present
//Software config
#define BUTTON_DEBOUNCE 5//Debounce milliseconds
//Hardware config
#define NEOPIXEL_PIN 1
#define BUTTON_PIN 3
#define DS2401_PIN 4
#define NUMPIXELS 1
/*
* Initialize instances/classes
*/
Bounce button = Bounce();
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(NUMPIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
OneWire ds(DS2401_PIN);
void setup()
{
uint32_t _now = millis();
//Start I2C
uint8_t _device_addr = EEPROM_DATA::get_device_addr();
//TinyWireS.begin(_device_id);
TinyWireS.begin(I2C_SLAVE_DEFAULT_ADDR);
TinyWireS.onReceive(I2C::receiveEvent);
TinyWireS.onRequest(I2C::requestEvent);
//Start NeoPixel
neopixel.begin();
neopixel.setPixelColor(NUMPIXELS-1, neopixel.Color(255, 255, 255) );
neopixel.show();
//Start Button
pinMode(BUTTON_PIN, INPUT_PULLUP);
button.attach(BUTTON_PIN);
button.interval(BUTTON_DEBOUNCE);
}
unsigned long lastBlink = 0;
boolean lastBlinkState;
void loop()
{
button.update();
if(VARS::led_needs_update)
{
led_update();
VARS::led_needs_update = false;
}
if(button.fell())
{
VARS::button_state = HIGH;
}
if(button.rose())
{
VARS::button_state = LOW;
}
//testblink
if ( (millis()-lastBlink) > 600)
{
if(lastBlinkState)
{
neopixel.setPixelColor(NUMPIXELS-1, 0,0,0 );
neopixel.show();
lastBlinkState = false;
}
else
{
led_update();
lastBlinkState = true;
}
lastBlink = millis();
}
// This needs to be here for the TinyWireS lib
TinyWireS_stop_check();
}
/*
* Helper functions
*/
void led_update()
{
neopixel.setPixelColor(NUMPIXELS-1,
VARS::led_red,
VARS::led_green,
VARS::led_blue
);
neopixel.show();
}
The Slave code - ATtiny 85 - namespaces.h
namespace VARS
{
//These are available in a I2C response
volatile boolean button_state = 0;
volatile int led_red = 0;
volatile int led_green = 0;
volatile int led_blue = 0;
uint8_t sn_family;
uint8_t sn_bit1;
//uint64_t serial_number = 0;
//int I2C_response_max_length = 4;//Count the above
//These are internal
volatile boolean led_needs_update = false;
int update_interval = 300;
unsigned long last_update = 0;
}
//===========================================================================
namespace EEPROM_DATA
{
uint8_t default_device_addr = I2C_SLAVE_DEFAULT_ADDR;
uint8_t this_device_address = 0x05;
bool device_addr_valid(uint8_t addr)
{
return (addr > 0x04 && addr < 0x7E);
}
bool store_device_addr(uint8_t new_device_addr)
{
if (!device_addr_valid(new_device_addr)) return false;//cancel if out of bounds. Assume corrupt data.
EEPROM.write(EEPROM_DATA::this_device_address, new_device_addr);//Set new i2c address into EEPROM
return true;
}
uint8_t get_device_addr()
{
uint8_t _addr = EEPROM.read(EEPROM_DATA::this_device_address);
if (!device_addr_valid(_addr))
{
_addr = EEPROM_DATA::default_device_addr;
store_device_addr(_addr); //update EEPROM with a valid/default device id.
}
return _addr;
}
}
//===========================================================================
namespace COMMANDS
{
enum {
assign_new_device_addr = 0xC1,
set_led_red = 0xC2,
set_led_green = 0xC3,
set_led_blue = 0xC4
};
bool do_command(uint8_t cmd, uint16_t cmd_data)
{
//interpret command.
switch (cmd)
{
//set new i2c slave adress.
//ATTENTION: After command is performed, the chip will need a hard reset.
case COMMANDS::assign_new_device_addr:
uint8_t new_device_addr;
new_device_addr = uint8_t(cmd_data & 0x007F);//ensure the address is 7-bit.
return EEPROM_DATA::store_device_addr(new_device_addr);//return true if saved, false if invalid address.
break;
case COMMANDS::set_led_red:
//if (cmd_data > 0x00FF) break;//ignore out of range data. Assume corrupt packet.
VARS::led_red = int(cmd_data);
VARS::led_needs_update = true;
break;
case COMMANDS::set_led_green:
//if (cmd_data > 0x00FF) break;//ignore out of range data. Assume corrupt packet.
VARS::led_green = int(cmd_data);
VARS::led_needs_update = true;
break;
case COMMANDS::set_led_blue:
//if (cmd_data > 0x00FF) break;//ignore out of range data. Assume corrupt packet.
VARS::led_blue = int(cmd_data);
VARS::led_needs_update = true;
break;
}
}
}
//===========================================================================
int pointer = 0;
namespace I2C{
//Gets called when the ATtiny receives a general i2c data request.
//Tx_stop is sent after end of call, so all bytes need to be sent before leaving.
void requestEvent()
{
//The number of bytes requested should NOT exceed the number of bytes returned
TinyWireS.send( VARS::button_state );
TinyWireS.send( VARS::led_red );
TinyWireS.send( VARS::led_green );
TinyWireS.send( VARS::led_blue );
}
//Gets called when the ATtiny recieves an i2c message THAT CONTAINS DATA from another device.
void receiveEvent(uint8_t byte_count)
{
//first byte is command, next 2 bytes is data for command. Total must equal 3 bytes.
if (!TinyWireS.available() || byte_count != 3 ) return;
//retrieve the command and 16-bit data.
//Incomming data is ordered CMD,Data-MSB,Data-LSB.
uint8_t cmd = TinyWireS.receive();
uint16_t cmd_data = (TinyWireS.receive() << 8);
cmd_data += TinyWireS.receive();
COMMANDS::do_command(cmd, cmd_data);
}
}