Use this code to reproduce our cups: (Every cup has the same code. You only need to change the constant myId for each different cup!)

#include <CapSense.h>
#include <Spi.h>
#include <mirf.h>
#include <nRF24L01.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
// Cup Network Configuration
#define cupCount 3
#define myId 2


// Sensor Configuration
#define Threshold 8
#define Samples 2
#define SensorTimeout 10


// Vibration Configuration
#define VibrationDuration 500
#define VibrationPeriod 1000 // Must be bigger than VibrationDuration
#define VibrationCount 3


// Matching Configuration
#define TimeBeforeHoldingStyleDecision 2000
#define TimeBeforeMatchingDecision 4000
#define TimeForMatchingExpiration 15000 // Must be bigger than TimeBeforeMatchingDecision


// Arduino PIN Configuration
#define PinLEDRed A0
#define PinLEDGreen A1
#define PinLEDBlue A3
#define PinVibrator 4
#define PinSensorSignal A4
#define PinSensorA 3
#define PinSensorB 6
#define PinSensorC 9
#define PinSensorD 7
#define PinRFCSN A5
#define PinRFCE A2


unsigned int tcnt2;
unsigned long currentTimestamp=0,myDecisionTimestamp=0,myMatchingTimestamp=0,myTimestamp=0,rTimestamp=0;
unsigned long rPacket, sPacket;
unsigned long knowledgeBase[cupCount][3] = {{ 0, 0, 0 } , { 0, 0, 0 } , { 0, 0, 0 }};
byte holdingStyles[16]={0,1,1,2,1,3,2,4,1,2,3,4,2,4,4,5};
byte myPreviousHoldingStyle=0,myCurrentHoldingStyle=0,myTouchedSensorCount=0;
byte myHoldingStyle=0,myColor=0,rId=0,rHoldingStyle=0,rColor=0,rMatchingId,rMatchingColor;
byte vibrationPeriodNumber,x=0;
boolean sensor1=false,sensor2=false,sensor3=false,sensor4=false;
boolean vibrationEnabled=false;
unsigned long vibrationStartTime=0;
boolean hasMatchCandidate=false;
byte matchCandidateId=0;


CapSense touchSensor1 = CapSense(PinSensorSignal,PinSensorA);
CapSense touchSensor2 = CapSense(PinSensorSignal,PinSensorB);
CapSense touchSensor3 = CapSense(PinSensorSignal,PinSensorC);
CapSense touchSensor4 = CapSense(PinSensorSignal,PinSensorD);


void setup(){
noInterrupts();
// Configure pins
pinMode(PinLEDRed, OUTPUT);
pinMode(PinLEDGreen, OUTPUT);
pinMode(PinLEDBlue, OUTPUT);
pinMode(PinVibrator, OUTPUT);
pinMode(PinSensorSignal, OUTPUT);
pinMode(PinSensorA, INPUT);
pinMode(PinSensorB, INPUT);
pinMode(PinSensorC, INPUT);
pinMode(PinSensorD, INPUT);


// Configure RF Module
Mirf.csnPin = PinRFCSN;
Mirf.cePin = PinRFCE;
Mirf.init();
Mirf.setRADDR((byte *)"cupNT");
Mirf.setTADDR((byte *)"cupNT");
Mirf.payload = sizeof(unsigned long);
Mirf.channel = 10;
Mirf.config();


// Configure Capasitive Sensors
touchSensor1.set_CS_AutocaL_Millis(0xFFFFFFFF);
touchSensor2.set_CS_AutocaL_Millis(0xFFFFFFFF);
touchSensor3.set_CS_AutocaL_Millis(0xFFFFFFFF);
touchSensor4.set_CS_AutocaL_Millis(0xFFFFFFFF);
touchSensor1.set_CS_Timeout_Millis(SensorTimeout);
touchSensor2.set_CS_Timeout_Millis(SensorTimeout);
touchSensor3.set_CS_Timeout_Millis(SensorTimeout);
touchSensor4.set_CS_Timeout_Millis(SensorTimeout);


interrupts();
lightUp(1); // Test LEDs
delay(500);
lightUp(2);
delay(500);
lightUp(3);
delay(500);
lightUp(7);
delay(500);
lightUp(0); // Turn off the LEDs


// Configure Timer Interrupt
TIMSK2 &= ~(1<<TOIE2);
TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
TCCR2B &= ~(1<<WGM22);
ASSR &= ~(1<<AS2);
TIMSK2 &= ~(1<<OCIE2A);
TCCR2B |= (1<<CS22) | (1<<CS20) | (1<<CS21);
tcnt2 =1;
TCNT2 = tcnt2;
TIMSK2 |= (1<<TOIE2);


// Configure & Enable Sleep Mode
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_enable();


// Enable Watchdog Timer
wdt_enable(WDTO_8S);
//Serial.begin(9600);
activateVibration(); // Test vibration
}


void loop() {
interrupts();
currentTimestamp=millis();
updateHoldingStyle();
vibration();
lightUp(myColor);
toSleep();
}


void sendPacket(byte id, unsigned long timestamp, byte holdingstyle, byte color){
sPacket=1000000000*id+(timestamp%9999999)*100+holdingstyle*10+color;
Mirf.send((byte *) &sPacket);
while(Mirf.isSending()){
TCNT2 = tcnt2;
}
}


void wakeupFunction(){
noInterrupts();
if(Mirf.dataReady()){
do{
Mirf.getData((byte *) &rPacket);
rId=floor(rPacket/1000000000);
if(rId==4 && myColor==0){
rMatchingColor=rPacket%10;
rMatchingId=((rPacket-rMatchingColor)/10)%10;
rTimestamp=((rPacket%1000000000)-rMatchingId*10-rMatchingColor)/100;
if(rMatchingId==myId){
myMatchingTimestamp=currentTimestamp-TimeBeforeMatchingDecision-50;
myColor=rMatchingColor;
}
}
else if(rId!=myId){
rColor=rPacket%10;
rHoldingStyle=((rPacket-rColor)/10)%10;
rTimestamp=((rPacket%1000000000)-rHoldingStyle*10-rColor)/100;
if(rTimestamp>knowledgeBase[rId][0] || rTimestamp<(knowledgeBase[rId][0]-100)){
knowledgeBase[rId][0]=rTimestamp;
knowledgeBase[rId][1]=rHoldingStyle;
knowledgeBase[rId][2]=rColor;
//Serial.println(rPacket);
}
}
}
while(!Mirf.rxFifoEmpty());
}
interrupts();
}


void toSleep(){
wdt_reset();
noInterrupts();
attachInterrupt(0,wakeupFunction,LOW);
interrupts();
sleep_mode();
sleep_disable();
detachInterrupt(0);
}


void checkSensors(){
myTouchedSensorCount=0;


sensor1 = (touchSensor1.capSense(Samples)>Threshold);
sensor2 = (touchSensor2.capSense(Samples)>Threshold);
sensor3 = (touchSensor3.capSense(Samples)>Threshold);
sensor4 = (touchSensor4.capSense(Samples)>Threshold);


if(sensor1) myTouchedSensorCount+=8;
if(sensor2) myTouchedSensorCount+=4;
if(sensor3) myTouchedSensorCount+=2;
if(sensor4) myTouchedSensorCount+=1;


myCurrentHoldingStyle=holdingStyles[myTouchedSensorCount];
}


void updateHoldingStyle(){
if(myCurrentHoldingStyle!=0 && myCurrentHoldingStyle!=myPreviousHoldingStyle){
myPreviousHoldingStyle=myCurrentHoldingStyle;
myDecisionTimestamp=currentTimestamp;
}
if((currentTimestamp-myDecisionTimestamp)>TimeBeforeHoldingStyleDecision){
myHoldingStyle=myPreviousHoldingStyle;
}
}


void checkForMatch(){
if(myColor>0 && (currentTimestamp-myMatchingTimestamp)>TimeForMatchingExpiration){ // Match expires
myColor=0;
hasMatchCandidate=false;
}
if(myColor==0 && hasMatchCandidate){
if(myHoldingStyle==knowledgeBase[matchCandidateId][1] && knowledgeBase[matchCandidateId][2]==0){
if((currentTimestamp-myMatchingTimestamp)>TimeBeforeMatchingDecision){
myColor=findUnusedColor();
sendPacket(4,myTimestamp,matchCandidateId,myColor);
}
}
else{
hasMatchCandidate=false;
myMatchingTimestamp=0;
deactivateVibration();
}
}
if(myColor==0 && !hasMatchCandidate){
if(myHoldingStyle==0 || myColor>0)
return;
for(byte i=0;i<cupCount;i++){
if(myId!=i && myHoldingStyle==knowledgeBase[i][1] && knowledgeBase[i][2]==0){
myMatchingTimestamp=currentTimestamp;
matchCandidateId=i;
activateVibration();
hasMatchCandidate=true;
return;
}
}
}
}


ISR(TIMER2_OVF_vect) {
wdt_reset();
noInterrupts();
if((++x%32)==0){
checkSensors();
checkForMatch();
myTimestamp=(currentTimestamp/100)%9999999;
knowledgeBase[myId][0]=myTimestamp;
knowledgeBase[myId][1]=myHoldingStyle;
knowledgeBase[myId][2]=myColor;
for(byte i=0;i<cupCount;i++){
sendPacket(i,knowledgeBase[i][0],knowledgeBase[i][1],knowledgeBase[i][2]);
}
}
TCNT2 = tcnt2;
interrupts();
}


void vibration(){
if(vibrationEnabled){
vibrationPeriodNumber=round((currentTimestamp-vibrationStartTime)/VibrationPeriod);
if(vibrationPeriodNumber>=VibrationCount)
vibrationEnabled=false;
else
{
if(((currentTimestamp-vibrationStartTime)%VibrationPeriod)<VibrationDuration)
digitalWrite(PinVibrator, HIGH);
else
digitalWrite(PinVibrator, LOW);
}
}
else{
digitalWrite(PinVibrator, LOW);
vibrationStartTime=0;
}
}


byte findUnusedColor(){
byte selectedColor=0;
while(selectedColor==0)
{
selectedColor=random(1,8);
for(byte i=0;i<cupCount;i++){
if(myId!=i && selectedColor==knowledgeBase[i][2]){
selectedColor=0;
break;
}
}
}
return selectedColor;
}


void activateVibration(){
vibrationStartTime=currentTimestamp;
vibrationEnabled=true;
}


void deactivateVibration(){
vibrationEnabled=false;
}


void lightUp(byte colorId){
switch (colorId) {
case 1: // RED
analogWrite(PinLEDRed, 255);
analogWrite(PinLEDGreen, -255);
analogWrite(PinLEDBlue, -255);
break;
case 2: // GREEN
analogWrite(PinLEDRed, -255);
analogWrite(PinLEDGreen, 255);
analogWrite(PinLEDBlue, -255);
break;
case 3: // BLUE
analogWrite(PinLEDRed, -255);
analogWrite(PinLEDGreen, -255);
analogWrite(PinLEDBlue, 255);
break;
case 4: // RED+GREEN
analogWrite(PinLEDRed, 255);
analogWrite(PinLEDGreen, 255);
analogWrite(PinLEDBlue, -255);
break;
case 5: // RED+BLUE
analogWrite(PinLEDRed, 255);
analogWrite(PinLEDGreen, -255);
analogWrite(PinLEDBlue, 255);
break;
case 6: // GREEN+BLUE
analogWrite(PinLEDRed, -255);
analogWrite(PinLEDGreen, 255);
analogWrite(PinLEDBlue, 255);
break;
case 7: // RED+GREEN+BLUE
analogWrite(PinLEDRed, 255);
analogWrite(PinLEDGreen, 255);
analogWrite(PinLEDBlue, 255);
break;
default: // GO OFF
analogWrite(PinLEDRed, -255);
analogWrite(PinLEDGreen, -255);
analogWrite(PinLEDBlue, -255);
}
}

Advertisements