chucktodd:
Don't use delay. design your code to use millis() with a sequencer. If your Arduino is a SLAVE I2C device, I2C communications are done by interrupt call back. So the Arduino environment receives and sends data when requested by the I2C master(RPi). Set up some global flags to indicate when a new command has been received, process the command in your main loop.
// The following typedef needs to be in the .h header file so that I can pass types as parameters
// move this typedef to "mydef.h" and save "mydef.h" in the same directory as your sketch .ino
typedef struct {
uint8_t cmd; // number representing some command
char data[10]; // a ten character buffer for optional data relating to the command
}CMD;
// start of main sketch file
#include "mydef.h"
#include <Wire.h>
#define NUMCMDS 5 // up to 4 pending commands
volatile static uint8_t cmdHead=0,cmdTail=0; // ring buffer indexes
volatile static CMD cmdList[NUMCMDS];
void addToCmdList( CMD tCmd){
uint8_t nCmd=(cmdHead+1)%NUMCMDS;
if(nCmd!=cmdTail){// room in list for new cmd
cmdList[nCmd].cmd = tCmd.cmd;
cmdList[nCmd].data = tCmd.data;
cmdHead=nCmd;
}
}
void onReceiveEvent( int numBytes){
if(numBytes>0){ // I2C sequence actually send a command, not just a ping
uint8_t Acmd=Wire.read();
CMD tCmd;
switch(Acmd){
case 0: // RPi send a command '0', lets define that to turn on the LED connected to pin 13
// immediate command, not worth queuing
digitalWrite(13,HIGH);
break;
case 1: // RPi send a '1' command, turn LED off
digitalWrite(13,LOW);
break;
case 2: // RPI sent a complex command that causes the Sun to Rise after a specified Delay
// parse the command from the I2C buffer and stuff it into our cmdList,
// a complex command that cannot execute within 10ms, queue it, let the main LOOP() handle it.
tCmd.cmd =Acmd;
Acmd = 0; // reuse variable
tCmd.data[Acmd]=0; // initialize
while(Wire.available()&&(Acmd<10)){
tCmd.data[Acmd++] = Wire.read();
}
addToCmdList(tCmd);
break;
default: ; // unknown cmd do nothing
}
void setup(){
digitalWrite(13,LOW); // set led driver off
pinMode(13,output);
Wire.begin(SLAVEID);
Wire.onReceive(onReceiveEvent()); // define my interrupt callback to respond to
// Wire Receive events
}
void loop(){
if(cmdTail!=cmdHead){ // I2C has received as least one new command
// pick it out of the buffer and process, clear space in CmdList
CMD tCmd; // local space for command
tCmd.cmd = cmdList[cmdTail].cmd;
tCmd.data = cmdList[cmdTail].data;
cmdTail = (cmdTail+1)%NUMCMDS;
processCmd(tCmd);
}
}
This code does not have any delay sequences, you need to define how the RPi interacts with the Arduino,
Can the RPi initiate SunRise then interrupt it?
Can the Arduino do Something Else while it is Lifting the Sun?
If you need to do a timed sequence of events then you need to define how the sequence will be affected by the RPi.
I have used a next sequence action structure. Basically a ring buffer that contains a tick counter and command;
typedef struct {
uint32_t tick; // millisecond for next command to execute
CMD tCmd;
}TICKS;
#define TICKDEPTH 9
TICKS tickBuff[TICKDEPTH];
uint8_t tickHead=0,tickTail=0;
loop(){
if(tickTail!=tickHead){ //something is queued to do!
if(millis()>tickBuff[tickTail].tick){ // activation time has arrived! yea!
process(tickBuff[tickTail].tCmd);
tickTail=(tickTail+1)%TICKDEPTH;
}
else { // nothing ready to do
}
else { // nothing it tickBuffer, nothing queued to Do!
}
void addToTick(uint32_t newTick, CMD newCmd){
uint8_t tHead = (tickHead+1)%TICKDEPTH; // tHead is only valid from 0.. (TICKDEPTH-1)
if(tHead!=tickTail){ // room in Tick ring buffer! yea!, add new Command.
tickBuff[tickHead].tick = newTick; // millisecond count for this command to activate
tickBuff[tickHead].tCmd = newCmd;
tickHead = tHead; // advance to next spot in tickBuff
}
else { // no room in buffer for command
}
}
This code may not compile, I haven't tried it, but it should give you an idea
Chuck.
I think I was able to get it working using your code, thanks!