Browse Source

Add singingPlant

jamesbowman 8 years ago
parent
commit
029c693a15
5 changed files with 645 additions and 11 deletions
  1. 7 5
      GD.cpp
  2. 1 6
      GD.h
  3. 395 0
      contrib/Guino_libray.ino
  4. 1 0
      publish.py
  5. 241 0
      singingPlant.ino

+ 7 - 5
GD.cpp

@@ -17,12 +17,14 @@ HardwareSPI SPI(1);
 #include <GD.h>
 #endif
 
+static byte GD_SEL_PIN;
 GDClass GD;
 
-void GDClass::begin()
+void GDClass::begin(int pin)
 {
   delay(250); // give Gameduino time to boot
-  pinMode(SS_PIN, OUTPUT);
+  GD_SEL_PIN = pin;
+  pinMode(GD_SEL_PIN, OUTPUT);
 #ifdef BOARD_maple
   SPI.begin(SPI_4_5MHZ, MSBFIRST, 0);
 #else
@@ -32,7 +34,7 @@ void GDClass::begin()
   SPI.setDataMode(SPI_MODE0);
   SPSR = (1 << SPI2X);
 #endif
-  digitalWrite(SS_PIN, HIGH);
+  digitalWrite(GD_SEL_PIN, HIGH);
 
   GD.wr(J1_RESET, 1);           // HALT coprocessor
   __wstart(RAM_SPR);            // Hide all sprites
@@ -63,7 +65,7 @@ void GDClass::end() {
 
 void GDClass::__start(unsigned int addr) // start an SPI transaction to addr
 {
-  digitalWrite(SS_PIN, LOW);
+  digitalWrite(GD_SEL_PIN, LOW);
   SPI.transfer(highByte(addr));
   SPI.transfer(lowByte(addr));  
 }
@@ -81,7 +83,7 @@ void GDClass::__wstartspr(unsigned int sprnum)
 
 void GDClass::__end() // end the SPI transaction
 {
-  digitalWrite(SS_PIN, HIGH);
+  digitalWrite(GD_SEL_PIN, HIGH);
 }
 
 byte GDClass::rd(unsigned int addr)

+ 1 - 6
GD.h

@@ -7,11 +7,6 @@
 #ifndef _GD_H_INCLUDED
 #define _GD_H_INCLUDED
 
-// define SS_PIN before including "GD.h" to override this
-#ifndef SS_PIN
-#define SS_PIN 9
-#endif
-
 #ifdef BOARD_maple
 
 #include "wirish.h"
@@ -40,7 +35,7 @@ struct sprplot
 
 class GDClass {
 public:
-  static void begin();
+  static void begin(int pin = 9);
   static void end();
   static void __start(unsigned int addr);
   static void __wstart(unsigned int addr);

+ 395 - 0
contrib/Guino_libray.ino

@@ -0,0 +1,395 @@
+/*
+  GUINO DASHBOARD TEMPLATE FOR THE ARDUINO. 
+ Done by Mads Hobye as a part of Instructables (AIR Program) & Medea (PhD Student).
+ Licens: Creative Commons — Attribution-ShareAlike
+ 
+ It should be used with the GUINO Dashboard app.
+ 
+ More info can be found here: www.hobye.dk
+ 
+ # This is the Guino Protocol Library should only be edited if you know what you are doing.
+ */
+
+#include <EasyTransfer.h>
+#include <EEPROM.h>
+
+#define guino_executed -1
+#define guino_init 0
+#define guino_addSlider 1
+#define guino_addButton 2
+#define guino_iamhere 3
+#define guino_addToggle 4
+#define guino_addRotarySlider 5
+#define guino_saveToBoard 6
+#define guino_setFixedGraphBuffer 8
+#define guino_clearLabel 7
+#define guino_addWaveform 9
+#define guino_addColumn 10
+#define guino_addSpacer 11
+#define guino_addMovingGraph 13
+#define guino_buttonPressed 14
+#define guino_addChar 15
+#define guino_setMin 16
+#define guino_setMax 17
+#define guino_setValue 20
+#define guino_addLabel 12
+#define guino_large 0
+#define guino_medium 1
+#define guino_small 2
+#define guino_setColor  21
+
+
+boolean guidino_initialized = false;
+
+//This function will write a 2 byte integer to the eeprom at the specified address and address + 1
+void EEPROMWriteInt(int p_address, int p_value)
+{
+  byte lowByte = ((p_value >> 0) & 0xFF);
+  byte highByte = ((p_value >> 8) & 0xFF);
+
+  EEPROM.write(p_address, lowByte);
+  EEPROM.write(p_address + 1, highByte);
+}
+
+//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
+unsigned int EEPROMReadInt(int p_address)
+{
+  byte lowByte = EEPROM.read(p_address);
+  byte highByte = EEPROM.read(p_address + 1);
+
+  return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
+}
+
+//create object
+EasyTransfer ET; 
+
+struct SEND_DATA_STRUCTURE
+{
+  //put your variable definitions here for the data you want to send
+  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
+  char cmd;
+  char item;
+  int value;
+};
+
+// Find a way to dynamically allocate memory
+int guino_maxGUIItems = 100;
+int guino_item_counter = 0;
+int *guino_item_values[100]; 
+int gTmpInt = 0; // temporary int for items without a variable
+boolean internalInit = true; // boolean to initialize before connecting to serial
+
+// COMMAND STRUCTURE
+
+//give a name to the group of data
+SEND_DATA_STRUCTURE guino_data;
+int eepromKey = 1234;
+void guino_update()
+{
+
+  while(Serial.available())
+  {
+
+    if(ET.receiveData())
+    {
+      switch (guino_data.cmd) 
+      {
+      case guino_init:
+
+        guino_item_counter = 0;
+        guidino_initialized = true;
+        gInit();
+        break;
+      case guino_setValue:
+        *guino_item_values[guino_data.item] = guino_data.value;
+        guino_data.cmd = guino_executed;
+        gItemUpdated(guino_data.item);
+        break;
+      case guino_buttonPressed:
+        gButtonPressed(guino_data.item);
+        break;
+      case guino_saveToBoard:
+        {
+
+          gInitEEprom();
+          for (int i =0; i < guino_item_counter;i++)
+          {
+            EEPROMWriteInt(i*2+2, *guino_item_values[i]);
+          }
+        }
+        break;
+      }
+    }
+  }
+}
+
+void gInitEEprom()
+{
+  if(EEPROMReadInt(0) != eepromKey)
+  {
+    EEPROMWriteInt(0, eepromKey);
+    for (int i =1; i <guino_maxGUIItems;i++)
+    {
+      EEPROMWriteInt(i*2+2, -3276);
+    }
+  }
+
+}
+
+void gSetColor(int _red, int _green, int _blue)
+{
+  gSendCommand(guino_setColor, 0, _red);
+  gSendCommand(guino_setColor, 1, _green);
+  gSendCommand(guino_setColor, 2, _blue);
+}
+
+void gGetSavedValue(int item_number, int *_variable)
+{
+
+  if(EEPROMReadInt(0) == eepromKey && internalInit)
+  {
+
+    int tmpVar =  EEPROMReadInt((item_number)*2+2);
+    if(tmpVar != -3276)
+      *_variable = tmpVar;
+  }
+
+}
+
+void gBegin(int _eepromKey)
+{
+
+  // Sets all pointers to a temporary value just to make sure no random memory pointers.
+  for(int i = 0; i < guino_maxGUIItems; i++)
+  {
+    guino_item_values[i] = &gTmpInt;
+  }
+  eepromKey = _eepromKey;
+
+  gInit(); // this one needs to run twice only way to work without serial connection.
+  internalInit = false;
+  Serial.begin(115200);
+  ET.begin(details(guino_data), &Serial);
+  gSendCommand(guino_executed, 0, 0);
+  gSendCommand(guino_executed, 0, 0);
+  gSendCommand(guino_executed, 0, 0);
+  gSendCommand(guino_iamhere, 0, 0); 
+
+}
+int gAddButton(char * _name)
+{
+  if(guino_maxGUIItems > guino_item_counter)
+  {
+    gSendCommand(guino_addButton,(byte)guino_item_counter,0);
+    for (int i = 0; i < strlen(_name); i++){
+      gSendCommand(guino_addChar,(byte)guino_item_counter,(int)_name[i]);
+    }
+    guino_item_counter++;
+    return guino_item_counter-1;
+  }
+  return -1;
+}
+
+
+void gAddColumn()
+{
+
+  gSendCommand(guino_addColumn,0,0);
+
+}
+
+
+
+
+int gAddLabel(char * _name, int _size)
+{
+  if(guino_maxGUIItems > guino_item_counter)
+  { 
+    gSendCommand(guino_addLabel,(byte)guino_item_counter,_size);
+
+    for (int i = 0; i < strlen(_name); i++){
+      gSendCommand(guino_addChar,(byte)guino_item_counter,(int)_name[i]);
+    }
+
+    guino_item_counter++;
+
+    return guino_item_counter-1;
+  }
+  return -1;
+
+
+} 
+int gAddSpacer(int _size)
+{
+  if(guino_maxGUIItems > guino_item_counter)
+  {
+    gSendCommand(guino_addSpacer,(byte)guino_item_counter,_size);
+
+    guino_item_counter++;
+    return guino_item_counter-1;
+  }
+  return -1;
+
+}   
+
+
+
+int gAddToggle(char * _name, int * _variable)
+{
+  if(guino_maxGUIItems > guino_item_counter)
+  {
+    guino_item_values[guino_item_counter] =_variable ;
+    gGetSavedValue(guino_item_counter, _variable);
+    gSendCommand(guino_addToggle,(byte)guino_item_counter,*_variable);
+
+    for (int i = 0; i < strlen(_name); i++){
+      gSendCommand(guino_addChar,(byte)guino_item_counter,(int)_name[i]);
+    }
+
+    guino_item_counter++;
+
+    return guino_item_counter-1;
+
+
+  }
+  return -1;
+}   
+
+int gAddFixedGraph(char * _name,int _min,int _max,int _bufferSize, int * _variable, int _size)
+{
+  if(guino_maxGUIItems > guino_item_counter)
+  {
+    gAddLabel(_name,guino_small);
+    guino_item_values[guino_item_counter] =_variable ;
+    gGetSavedValue(guino_item_counter, _variable);
+    gSendCommand(guino_addWaveform,(byte)guino_item_counter,_size);
+    gSendCommand(guino_setMax,(byte)guino_item_counter,_max);
+    gSendCommand(guino_setMin,(byte)guino_item_counter,_min);
+    gSendCommand(guino_setFixedGraphBuffer,(byte)guino_item_counter,_bufferSize);
+
+
+    guino_item_counter++;
+
+    return guino_item_counter-1;
+  }
+  return -1;
+}
+
+int gAddMovingGraph(char * _name,int _min,int _max, int * _variable, int _size)
+{
+  if(guino_maxGUIItems > guino_item_counter)
+  {
+    gAddLabel(_name,guino_small);
+    guino_item_values[guino_item_counter] =_variable ;
+    gGetSavedValue(guino_item_counter, _variable);
+    gSendCommand(guino_addMovingGraph,(byte)guino_item_counter,_size);
+    gSendCommand(guino_setMax,(byte)guino_item_counter,_max);
+    gSendCommand(guino_setMin,(byte)guino_item_counter,_min);
+
+
+    guino_item_counter++;
+
+    return guino_item_counter-1;
+  }
+  return -1;
+
+
+}   
+
+
+int gUpdateLabel(int _item, char * _text)
+{
+
+  gSendCommand(guino_clearLabel,(byte)_item,0);
+  for (int i = 0; i < strlen(_text); i++){
+    gSendCommand(guino_addChar,(byte)_item,(int)_text[i]);
+  }
+
+
+
+}
+
+
+
+int gAddRotarySlider(int _min,int _max, char * _name, int * _variable)
+{
+  if(guino_maxGUIItems > guino_item_counter)
+  {
+    guino_item_values[guino_item_counter] =_variable ;
+    gGetSavedValue(guino_item_counter, _variable);
+    gSendCommand(guino_addRotarySlider,(byte)guino_item_counter,*_variable);
+    gSendCommand(guino_setMax,(byte)guino_item_counter,_max);
+    gSendCommand(guino_setMin,(byte)guino_item_counter,_min);
+    for (int i = 0; i < strlen(_name); i++){
+      gSendCommand(guino_addChar,(byte)guino_item_counter,(int)_name[i]);
+    }
+
+    guino_item_counter++;
+    gUpdateValue(_variable);
+    return guino_item_counter-1;
+  }
+  return -1;
+
+}
+
+int gAddSlider(int _min,int _max, char * _name, int * _variable)
+{
+  if(guino_maxGUIItems > guino_item_counter)
+  {
+    guino_item_values[guino_item_counter] =_variable ;
+    gGetSavedValue(guino_item_counter, _variable);
+    gSendCommand(guino_addSlider,(byte)guino_item_counter,*_variable);
+    gSendCommand(guino_setMax,(byte)guino_item_counter,_max);
+    gSendCommand(guino_setMin,(byte)guino_item_counter,_min);
+    for (int i = 0; i < strlen(_name); i++){
+      gSendCommand(guino_addChar,(byte)guino_item_counter,(int)_name[i]);
+    }
+
+    guino_item_counter++;
+    gUpdateValue(_variable);
+    return guino_item_counter-1;
+  }
+  return -1;
+
+}
+
+void gUpdateValue(int _item)
+{
+
+  gSendCommand(guino_setValue,_item, *guino_item_values[_item]); 
+}
+
+
+void gUpdateValue(int * _variable)
+{
+
+  int current_id = -1;
+  for(int i = 0; i < guino_item_counter; i++)
+  {
+
+    if(guino_item_values[i] == _variable)
+    {
+
+      current_id = i;
+      gUpdateValue(current_id);
+    }
+  }
+  // if(current_id != -1)
+
+}
+
+
+
+void gSendCommand(byte _cmd, byte _item, int _value)
+{
+  if(!internalInit && (guidino_initialized || guino_executed || _cmd == guino_iamhere)  )
+  {
+    guino_data.cmd = _cmd;
+    guino_data.item = _item;
+    guino_data.value = _value;
+    ET.sendData();
+  }
+
+}
+
+

+ 1 - 0
publish.py

@@ -12,6 +12,7 @@ inventory = {
     '4.Demo'            : "ball chessboard dna spectrum cp437 watterott",
     '5.Games'           : "asteroids frogger chopper manicminer",
     '6.Tools'           : "selftest screenshot memloader joytest",
+    '7.Contrib'         : "singingPlant",
 }
 
 import zipfile

+ 241 - 0
singingPlant.ino

@@ -0,0 +1,241 @@
+#include <SPI.h>
+#include <GD.h>
+
+#include "mont.h"
+
+long started =0;
+int ticks = 0;
+const uint8_t *pc;
+
+// visualize voice (v) at amplitude (a) on an 8x8 grid
+
+#define SET(x,y) (x |=(1<<y))       //-Bit set/clear macros
+#define CLR(x,y) (x &= (~(1<<y)))   // |
+#define CHK(x,y) (x & (1<<y))       // |
+#define TOG(x,y) (x^=(1<<y))        //-+
+
+#define N 120                       // How many frequencies
+
+float results[N];                   // Filtered result buffer
+int sizeOfArray = N;
+int fixedGraph = 0;
+int topPoint = 0;
+int topPointValue = 0;
+int topPointInterPolated = 0;
+int baseline = 0;
+int baselineMax =0;
+int value = 0;
+
+void visualize(byte v, byte a)
+{
+  int x = 64 + ((v & 7) * 34);
+  int y = 14 + ((v >> 3) * 34);
+  byte sprnum = (v << 2); // draw each voice using four sprites
+  GD.sprite(sprnum++, x + 16, y + 16, a, 0, 0);
+  GD.sprite(sprnum++, x +  0, y + 16, a, 0, 2);
+  GD.sprite(sprnum++, x + 16, y +  0, a, 0, 4);
+  GD.sprite(sprnum++, x +  0, y +  0, a, 0, 6);
+}
+
+void setup()
+{
+  gBegin(34526);
+  // max and min buttons
+
+  pinMode(A1, INPUT);
+  pinMode(A2, INPUT);
+  digitalWrite(A1, HIGH);
+  digitalWrite(A2, HIGH);
+  int i;
+
+  GD.begin(4);
+
+  GD.wr16(RAM_SPRPAL + 2 * 255, TRANSPARENT);
+
+  // draw 32 circles into 32 sprite images
+  for (i = 0; i < 32; i++) {
+    GD.wr16(RAM_SPRPAL + 2 * i, RGB(8 * i, 64, 255 - 8 * i));
+    int dst = RAM_SPRIMG + 256 * i;
+    GD.__wstart(dst);
+    byte x, y;
+    int r2 = min(i * i, 256);
+    for (y = 0; y < 16; y++) {
+      for (x = 0; x < 16; x++) {
+        byte pixel;
+        if ((x * x + y * y) <= r2)
+          pixel = i;    // use color above
+        else
+          pixel = 0xff; // transparent
+        SPI.transfer(pixel);
+      }
+    }
+    GD.__end();
+  }
+
+  for (i = 0; i < 64; i++)
+    visualize(i, 0);
+
+  pc = mont;
+
+  TCCR1A=0b10000010;        //-Set up frequency generator
+  TCCR1B=0b00011001;        //-+
+
+  ICR1=110;
+  OCR1A=55;
+
+  pinMode(9,OUTPUT);        //-Signal generator pin
+
+  for(int i=0;i<N;i++)      //-Preset results
+    results[i]=0;         //-+
+}
+
+byte amp[64];       // current voice amplitude
+byte target[64];    // target voice amplitude
+
+// Set volume for voice v to a
+void setvol(byte v, byte a)
+{
+  GD.__wstart(VOICES + (v << 2) + 2);
+  SPI.transfer(a);
+  SPI.transfer(a);
+  GD.__end();
+}
+
+void adsr()  // handle ADSR for 64 voices
+{
+  byte v;
+  for (v = 0; v < 64; v++) {
+    int d = target[v] - amp[v]; // +ve means need to increase
+    if (d) {
+      if (d > 0)
+        amp[v] += 4;  // attack
+      else
+        amp[v]--;     // decay
+      if(value < 5)
+      {
+        setvol(v, 0);
+      }
+      else
+      {
+        setvol(v, amp[v]*((float)value/800+0.2));
+      }
+      visualize(v, amp[v]);
+    }
+  }
+}
+
+unsigned int d=0;
+
+boolean btnMax = false;
+boolean btnMin = false;
+
+void loop()
+{
+  guino_update();
+  if (pc >= mont + sizeof(mont) - 4) {
+    started = millis();
+    ticks = 0;
+    pc = mont;
+  }
+  if (millis() < (started + ticks * 5)) {
+    adsr();
+  } else {
+    byte cmd = pgm_read_byte_near(pc++);	// upper 2 bits are command code
+    if ((cmd & 0xc0) == 0) {
+      // Command 0x00: pause N*5 milliseconds
+      ticks += (cmd & 63);
+    } else {
+      byte v = (cmd & 63);
+      byte a;
+      if ((cmd & 0xc0) == 0x40) {
+	// Command 0x40: silence voice
+	target[v] = 0;
+      } else if ((cmd & 0xc0) == 0x80) {
+	// Command 0x80: set voice frequency and amplitude
+	byte flo = pgm_read_byte_near(pc++);
+	if (value < 0)
+	  flo = 0;
+	byte fhi = pgm_read_byte_near(pc++) * ((float) value / 300.0f);
+	GD.__wstart(VOICES + 4 * v);
+	SPI.transfer(flo);
+	SPI.transfer(fhi);
+	GD.__end();
+	target[v] = pgm_read_byte_near(pc++);
+      }
+    }
+  }
+
+  if (!(d < N)) {
+    d = 0;
+    topPoint = 0;
+    topPointValue = 0;
+  }
+  int v = analogRead(0);	//-Read response signal
+  results[d] = results[d] * 0.5 + (float) (v) * 0.5;	//Filter results
+  fixedGraph = round(results[d]);
+  gUpdateValue(&fixedGraph);
+  if (topPointValue < results[d]) {
+    topPointValue = results[d];
+    topPoint = d;
+  }
+
+  CLR(TCCR1B, 0);		//-Stop generator
+  TCNT1 = 0;			//-Reload new frequency
+  ICR1 = d;			// |
+  OCR1A = d / 2;		//-+
+  SET(TCCR1B, 0);		//-Restart generator
+
+  d++;
+  if (d == N) {
+    topPointInterPolated = topPointInterPolated * 0.5f +
+	((topPoint +
+	  results[topPoint] / results[topPoint + 1] * results[topPoint - 1] /
+	  results[topPoint]) * 10.0f) * 0.5f;
+    value =
+	max(0, map(topPointInterPolated, baseline, baselineMax, 0, 1000));
+    gUpdateValue(&topPoint);
+    gUpdateValue(&value);
+    gUpdateValue(&topPointInterPolated);
+
+    if (!digitalRead(A1) && btnMin) {
+      baseline = topPointInterPolated;
+      gUpdateValue(&baseline);
+    }
+
+    btnMin = !digitalRead(A1);
+
+    if (!digitalRead(A2) && btnMax) {
+      baselineMax = topPointInterPolated;
+      gUpdateValue(&baselineMax);
+    }
+
+    btnMax = !digitalRead(A2);
+  }
+}
+
+// This is where you setup your interface
+void gInit()
+{
+  gAddLabel("DisneyTouch", 1);
+
+  gAddSpacer(1);
+
+  gAddSpacer(1);
+  gAddFixedGraph("FIXED GRPAPH", -500, 1000, N, &fixedGraph, 40);
+  gAddSlider(0, N, "TOP", &topPoint);
+  gAddSlider(0, N * 10, "Interpolated", &topPointInterPolated);
+  gAddSlider(0, 3000, "Baseline", &baseline);
+  gAddSlider(0, 3000, "BaselineMax", &baselineMax);
+  gAddSlider(0, 1000, "Value", &value);
+}
+
+// Method called everytime a button has been pressed in the interface.
+void gButtonPressed(int id)
+{
+
+}
+
+void gItemUpdated(int id)
+{
+
+}