Hardware components | ||||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
Software apps and online services | ||||||
![]() |
|
Christmas is already here, time to be close to the family, share, and create new memorable moments. We all already know the adage, one picture is worth a thousand words. So we decide to create an easy project with which you can show photos or images using people’s interaction with the Michi board, making them more familiar with this kind of device and encouraging them to start in the electronics world.
Following this project, you will create an interactive system where you will be the one creating and using your own buttons, but how is this possible?
MichiMichi is an educational board that has resistive sensors easy to use for the user to create their own buttons or sensors, you can use any material that conducts electricity (will not hurt) as clay for kids, fruits, metal objects, and more. It is great to have a funny moment with the kids and the family thinking about shapes, materials, colors, etc., and creating your own buttons, connecting them with alligator cables to the “garritas” of the Michi, and start using them.
On the back of Michi, you have 9 digital pins that can be used to read and write digital signals, for controlling and monitoring external devices, like your Christmas lights.
Bast Pro MiniBast Pro Mini is a tiny dev board with a powerful microcontroller which is useful for this kind of project when you don't have enough space for bigger boards.
In this project, Bast Pro Mini is the brain of our screen, the one that indicates the screen what to show and where to search for the pictures. For this, we use a simple sketch to read a picture in BMP format from an SD card, show it on the screen using SPI communication, and all this depending on which "garrita" of the Michi you are using.
We used a TFT Screen for this case since it was the screen we had available, but you can always use a different screen technology, size, and resolution, this applies to the SD card as well, in the market, you can find different screens or SD/MicroSD adaptors. The decision is on you and your needs.
Combining them, you have a system where you press your own-made button connected to a "garrita" on the Michi, the Michi sends a signal to the Bast Pro Mini through its digital I/O and the Bast Pro Mini decides which image showed depending on the button pressed.
Now is the time to connect wires and plug your board into the PC, download the correct codes for each board, and upload the code that you can find below in this article.
With this, you can create fun games or activities with your family, like pressing one of your buttons and showing a picture or message indicating an action to do with the following family member, like a hug, dance, make a compliment, and more. We even think you can hang this on your Christmas tree or decoration and convert them into a button so you can touch it and create an electric signal that Michi can understand, as shown in this article.
You can always use a bigger screen for better visualization, add more actuators, and more.
Below you can find the diagram where you can see how the connections are made, the code, and more.
Here are more projects with Michi and Bast Pro Mini:
https://www.hackster.io/electronic-cats/getting-started-michi-c2534b
https://www.hackster.io/electronic-cats/showcasing-michi-da89ca
https://www.hackster.io/education-team/how-to-create-a-smoking-cat-ghost-1d6da2
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SD.h>
#include <SPI.h>
// TFT display and SD card will share the hardware SPI interface.
// Hardware SPI pins are specific to the Arduino board type.
#define SD_CS 4 // Chip select line for SD card
#define TFT_CS 10 // Chip select line for TFT display
#define TFT_DC 8 // Data/command line for TFT
#define TFT_RST 9 // Reset line for TFT (or connect to +5V)
int inPin = 7;
int i=0;
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
File root;
File myFile;
void setup(void) {
delay(1000);
Serial.begin(115200);
delay(1000);
Serial.println("=====================");
Serial.println("- setup() -");
tft.initR(INITR_BLACKTAB);
Serial.println("tft: "
+ String(tft.width()) + " : "
+ String(tft.height()));
tft.fillScreen(ST7735_BLACK);
tft.setTextWrap(true);
tft.setTextColor(ST77XX_WHITE);
tft.setCursor(0, 0);
tft.print("Arduino nano RP2040 Connect");
//----------------------------------------
Serial.println("Initializing SD card...");
if (!SD.begin(SD_CS)){
Serial.println("SD.begin() failed!");
}
else{
Serial.println("SD.begin() Success.");
root = SD.open("/");
printDirectory(root, 0);
}
//----------------------------------------
Serial.println("\n- End of setup() -\n");
pinMode(inPin, INPUT);
}
const int NO_OF_BMP = 8;
char* bmpFiles[NO_OF_BMP] = {"1.bmp", "2.bmp","3.bmp","4.bmp","5.bmp","6.bmp","7.bmp","8.bmp"};
void loop() {
int image_to_show = digitalRead(inPin);
Serial.print("image_to_show ");
Serial.println(image_to_show);
/* for(int i=0; i<NO_OF_BMP; i++){
bmpDraw(bmpFiles[i], 0, 0);
delay(2000);
}*/
if (image_to_show == 1){
i++;
Serial.println(i);
if(i>NO_OF_BMP){
i=0;
}
}
bmpDraw(bmpFiles[i], 0, 0);
delay (500);
}
//
// printDirectory() copy from:
// Examples > SD > listfiles
//
void printDirectory(File dir, int numTabs) {
while (true) {
File entry = dir.openNextFile();
if (! entry) {
// no more files
break;
}
for (uint8_t i = 0; i < numTabs; i++) {
Serial.print('\t');
}
Serial.print(entry.name());
if (entry.isDirectory()) {
Serial.println("/");
printDirectory(entry, numTabs + 1);
} else {
// files have sizes, directories do not
Serial.print("\t\t");
Serial.println(entry.size(), DEC);
}
entry.close();
}
}
//
// bmpDraw() copy from:
// Examples under "Adafruit ST7735 and ST7789 Library" > shieldtest
//
// This function opens a Windows Bitmap (BMP) file and
// displays it at the given coordinates. It's sped up
// by reading many pixels worth of data at a time
// (rather than pixel by pixel). Increasing the buffer
// size takes more of the Arduino's precious RAM but
// makes loading a little faster. 20 pixels seems a
// good balance.
#define BUFFPIXEL 15
void bmpDraw(char *filename, uint8_t x, uint8_t y) {
File bmpFile;
int bmpWidth, bmpHeight; // W+H in pixels
uint8_t bmpDepth; // Bit depth (currently must be 24)
uint32_t bmpImageoffset; // Start of image data in file
uint32_t rowSize; // Not always = bmpWidth; may have padding
uint8_t sdbuffer[3*BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
uint8_t buffidx = sizeof(sdbuffer); // Current position in sdbuffer
boolean goodBmp = false; // Set to true on valid header parse
boolean flip = true; // BMP is stored bottom-to-top
int w, h, row, col;
uint8_t r, g, b;
uint32_t pos = 0, startTime = millis();
if((x >= tft.width()) || (y >= tft.height())) return;
Serial.println();
Serial.print("Loading image '");
Serial.print(filename);
Serial.println('\'');
// Open requested file on SD card
if ((bmpFile = SD.open(filename)) == NULL) {
Serial.print("File not found");
return;
}
// Parse BMP header
if(read16(bmpFile) == 0x4D42) { // BMP signature
Serial.print("File size: "); Serial.println(read32(bmpFile));
(void)read32(bmpFile); // Read & ignore creator bytes
bmpImageoffset = read32(bmpFile); // Start of image data
Serial.print("Image Offset: "); Serial.println(bmpImageoffset, DEC);
// Read DIB header
Serial.print("Header size: "); Serial.println(read32(bmpFile));
bmpWidth = read32(bmpFile);
bmpHeight = read32(bmpFile);
if(read16(bmpFile) == 1) { // # planes -- must be '1'
bmpDepth = read16(bmpFile); // bits per pixel
Serial.print("Bit Depth: "); Serial.println(bmpDepth);
if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed
goodBmp = true; // Supported BMP format -- proceed!
Serial.print("Image size: ");
Serial.print(bmpWidth);
Serial.print('x');
Serial.println(bmpHeight);
// BMP rows are padded (if needed) to 4-byte boundary
rowSize = (bmpWidth * 3 + 3) & ~3;
// If bmpHeight is negative, image is in top-down order.
// This is not canon but has been observed in the wild.
if(bmpHeight < 0) {
bmpHeight = -bmpHeight;
flip = false;
}
// Crop area to be loaded
w = bmpWidth;
h = bmpHeight;
if((x+w-1) >= tft.width()) w = tft.width() - x;
if((y+h-1) >= tft.height()) h = tft.height() - y;
// Set TFT address window to clipped image bounds
tft.startWrite();
tft.setAddrWindow(x, y, w, h);
for (row=0; row<h; row++) { // For each scanline...
// Seek to start of scan line. It might seem labor-
// intensive to be doing this on every line, but this
// method covers a lot of gritty details like cropping
// and scanline padding. Also, the seek only takes
// place if the file position actually needs to change
// (avoids a lot of cluster math in SD library).
if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
else // Bitmap is stored top-to-bottom
pos = bmpImageoffset + row * rowSize;
if(bmpFile.position() != pos) { // Need seek?
tft.endWrite();
bmpFile.seek(pos);
buffidx = sizeof(sdbuffer); // Force buffer reload
}
for (col=0; col<w; col++) { // For each pixel...
// Time to read more pixel data?
if (buffidx >= sizeof(sdbuffer)) { // Indeed
bmpFile.read(sdbuffer, sizeof(sdbuffer));
buffidx = 0; // Set index to beginning
tft.startWrite();
}
// Convert pixel from BMP to TFT format, push to display
b = sdbuffer[buffidx++];
g = sdbuffer[buffidx++];
r = sdbuffer[buffidx++];
tft.pushColor(tft.color565(r,g,b));
} // end pixel
} // end scanline
tft.endWrite();
Serial.print("Loaded in ");
Serial.print(millis() - startTime);
Serial.println(" ms");
} // end goodBmp
}
}
bmpFile.close();
if(!goodBmp) Serial.println("BMP format not recognized.");
}
// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.
uint16_t read16(File f) {
uint16_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read(); // MSB
return result;
}
uint32_t read32(File f) {
uint32_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read();
((uint8_t *)&result)[2] = f.read();
((uint8_t *)&result)[3] = f.read(); // MSB
return result;
}
/*
/////////////////////////////////////////////////
/////////////HOW TO EDIT THE KEYS ///////////////
/////////////////////////////////////////////////
- Edit keys in the settings.h file
- that file should be open in a tab above (in Arduino IDE)
- more instructions are in that file
//////////////////////////////////////////////////
///////// MICHI FIRMWARE v1.0.0 ////////////
//////////////////////////////////////////////////
/************************************************************
Michi - Using the Michi you can make anything into a key
just by connecting a few alligator clips
Andres Sabas @ Electronic Cats
Original Creation Date: Sept 6, 2022
https://github.com/ElectronicCats/MeowMeow
This example demonstrates how to use Meow Meow
Development environment specifics:
IDE: Arduino 1.8.19
Hardware Platform:
Michi
- RP2040
This code is beerware; if you see me (or any other Electronic Cats
member) at the local, and you've found our code helpful,
please buy us a round!
Distributed as-is; no warranty is given.
Based in the work by:
by: Eric Rosenbaum, Jay Silver, and Jim Lindblom
MIT Media Lab & Sparkfun
start date: 2/16/2012
release: 7/5/2012
***************************************************************************/
/////////////////////////
// DEBUG DEFINITIONS ////
/////////////////////////
//#define DEBUG
//#define DEBUG2
//#define DEBUG3
//#define DEBUG_TIMING
//#define DEBUG_MOUSE
//#define DEBUG_TIMING2
////////////////////////
// DEFINED CONSTANTS////
////////////////////////
#define BUFFER_LENGTH 3 // 3 bytes gives us 24 samples
#define NUM_INPUTS 10 // 6 on the front + 12 on the back
#define NUM_OUTPUTS 10 //
//#define TARGET_LOOP_TIME 694 // (1/60 seconds) / 24 samples = 694 microseconds per sample
//#define TARGET_LOOP_TIME 758 // (1/55 seconds) / 24 samples = 758 microseconds per sample
#define TARGET_LOOP_TIME 744 // (1/56 seconds) / 24 samples = 744 microseconds per sample
// id numbers for mouse movement inputs (used in settings.h)
#define MOUSE_MOVE_UP -1
#define MOUSE_MOVE_DOWN -2
#define MOUSE_MOVE_LEFT -3
#define MOUSE_MOVE_RIGHT -4
#include <Keyboard.h>
#include <Mouse.h>
#include "settings.h"
/////////////////////////
// STRUCT ///////////////
/////////////////////////
typedef struct {
byte pinNumber;
int keyCode;
byte measurementBuffer[BUFFER_LENGTH];
boolean oldestMeasurement;
byte bufferSum;
boolean pressed;
boolean prevPressed;
boolean isMouseMotion;
boolean isMouseButton;
boolean isKey;
}
MakeyMakeyInput;
MakeyMakeyInput inputs[NUM_INPUTS];
///////////////////////////////////
// VARIABLES //////////////////////
///////////////////////////////////
int bufferIndex = 0;
byte byteCounter = 0;
byte bitCounter = 0;
int mouseMovementCounter = 0; // for sending mouse movement events at a slower interval
int pressThreshold;
int releaseThreshold;
boolean inputChanged;
int mouseHoldCount[NUM_INPUTS]; // used to store mouse movement hold data
// Pin Numbers
// input pin numbers for kickstarter production board
int pinNumbers[NUM_INPUTS] = {
27, 25, 29, 23, 19, 21, // top of makey makey board
17, 14, 12, 10 // left side of female header, KEYBOARD
// right side of female header, MOUSE
};
int pinNumbers2[NUM_OUTPUTS] = {
9, 11, 13, 16, 18, 20, // top of makey makey board
22, 24, 26, 28 // left side of female header, KEYBOARD
// right side of female header, MOUSE
};
// input status LED pin numbers
const int inputLED_a = 15;
const int inputLED_b = 8;
const int inputLED_c = 7;
const int outputK = 0;
const int outputM = 1;
byte ledCycleCounter = 0;
// timing
int loopTime = 0;
int prevTime = 0;
int loopCounter = 0;
///////////////////////////
// FUNCTIONS //////////////
///////////////////////////
void initializeArduino();
void initializeInputs();
void updateMeasurementBuffers();
void updateBufferSums();
void updateBufferIndex();
void updateInputStates();
void sendMouseButtonEvents();
void sendMouseMovementEvents();
void addDelay();
void cycleLEDs();
void danceLeds();
void updateOutLEDs();
//////////////////////
// SETUP /////////////
//////////////////////
void setup()
{
initializeArduino();
initializeInputs();
danceLeds();
pinMode(D2, OUTPUT);
}
////////////////////
// MAIN LOOP ///////
////////////////////
void loop()
{
updateMeasurementBuffers();
updateBufferSums();
updateBufferIndex();
updateInputStates();
sendMouseButtonEvents();
sendMouseMovementEvents();
cycleLEDs();
updateOutLEDs();
addDelay();
}
//////////////////////////
// INITIALIZE ARDUINO
//////////////////////////
void initializeArduino() {
#ifdef DEBUG
Serial.begin(9600); // Serial for debugging
#endif
/* Set up input pins
DEactivate the internal pull-ups, since we're using external resistors */
for (int i=0; i<NUM_INPUTS; i++)
{
pinMode(pinNumbers[i], INPUT);
digitalWrite(pinNumbers[i], LOW);
}
for (int i=0; i<NUM_OUTPUTS; i++)
{
pinMode(pinNumbers2[i], OUTPUT);
digitalWrite(pinNumbers2[i], HIGH);
}
pinMode(inputLED_a, INPUT);
pinMode(inputLED_b, INPUT);
pinMode(inputLED_c, INPUT);
digitalWrite(inputLED_a, LOW);
digitalWrite(inputLED_b, LOW);
digitalWrite(inputLED_c, LOW);
pinMode(outputK, OUTPUT);
pinMode(outputM, OUTPUT);
digitalWrite(outputK, LOW);
digitalWrite(outputM, LOW);
#ifdef DEBUG
delay(4000); // allow us time to reprogram in case things are freaking out
#endif
Keyboard.begin();
Mouse.begin();
}
///////////////////////////
// INITIALIZE INPUTS
///////////////////////////
void initializeInputs() {
float thresholdPerc = SWITCH_THRESHOLD_OFFSET_PERC;
float thresholdCenterBias = SWITCH_THRESHOLD_CENTER_BIAS/50.0;
float pressThresholdAmount = (BUFFER_LENGTH * 8) * (thresholdPerc / 100.0);
float thresholdCenter = ( (BUFFER_LENGTH * 8) / 2.0 ) * (thresholdCenterBias);
pressThreshold = int(thresholdCenter + pressThresholdAmount);
releaseThreshold = int(thresholdCenter - pressThresholdAmount);
#ifdef DEBUG
Serial.println(pressThreshold);
Serial.println(releaseThreshold);
#endif
for (int i=0; i<NUM_INPUTS; i++) {
inputs[i].pinNumber = pinNumbers[i];
inputs[i].keyCode = keyCodes[i];
for (int j=0; j<BUFFER_LENGTH; j++) {
inputs[i].measurementBuffer[j] = 0;
}
inputs[i].oldestMeasurement = 0;
inputs[i].bufferSum = 0;
inputs[i].pressed = false;
inputs[i].prevPressed = false;
inputs[i].isMouseMotion = false;
inputs[i].isMouseButton = false;
inputs[i].isKey = false;
if (inputs[i].keyCode < 0) {
#ifdef DEBUG_MOUSE
Serial.println("GOT IT");
#endif
inputs[i].isMouseMotion = true;
}
else if ((inputs[i].keyCode == MOUSE_LEFT) || (inputs[i].keyCode == MOUSE_RIGHT)) {
inputs[i].isMouseButton = true;
}
else {
inputs[i].isKey = true;
}
#ifdef DEBUG
Serial.println(i);
#endif
}
}
//////////////////////////////
// UPDATE MEASUREMENT BUFFERS
//////////////////////////////
void updateMeasurementBuffers() {
for (int i=0; i<NUM_INPUTS; i++) {
// store the oldest measurement, which is the one at the current index,
// before we update it to the new one
// we use oldest measurement in updateBufferSums
byte currentByte = inputs[i].measurementBuffer[byteCounter];
inputs[i].oldestMeasurement = (currentByte >> bitCounter) & 0x01;
// make the new measurement
boolean newMeasurement = digitalRead(inputs[i].pinNumber);
// invert so that true means the switch is closed
newMeasurement = !newMeasurement;
// store it
if (newMeasurement) {
currentByte |= (1<<bitCounter);
}
else {
currentByte &= ~(1<<bitCounter);
}
inputs[i].measurementBuffer[byteCounter] = currentByte;
}
}
///////////////////////////
// UPDATE BUFFER SUMS
///////////////////////////
void updateBufferSums() {
// the bufferSum is a running tally of the entire measurementBuffer
// add the new measurement and subtract the old one
for (int i=0; i<NUM_INPUTS; i++) {
byte currentByte = inputs[i].measurementBuffer[byteCounter];
boolean currentMeasurement = (currentByte >> bitCounter) & 0x01;
if (currentMeasurement) {
inputs[i].bufferSum++;
}
if (inputs[i].oldestMeasurement) {
inputs[i].bufferSum--;
}
}
}
///////////////////////////
// UPDATE BUFFER INDEX
///////////////////////////
void updateBufferIndex() {
bitCounter++;
if (bitCounter == 8) {
bitCounter = 0;
byteCounter++;
if (byteCounter == BUFFER_LENGTH) {
byteCounter = 0;
}
}
}
///////////////////////////
// UPDATE INPUT STATES
///////////////////////////
void updateInputStates() {
inputChanged = false;
for (int i=0; i<NUM_INPUTS; i++) {
inputs[i].prevPressed = inputs[i].pressed; // store previous pressed state (only used for mouse buttons)
if (inputs[i].pressed) {
if (inputs[i].bufferSum < releaseThreshold) {
inputChanged = true;
inputs[i].pressed = false;
digitalWrite(D2, HIGH);
delay(1000);
digitalWrite(D2, LOW);
if (inputs[i].isKey) {
Keyboard.release(inputs[i].keyCode);
digitalWrite(D2, LOW);
}
if (inputs[i].isMouseMotion) {
mouseHoldCount[i] = 0; // input becomes released, reset mouse hold
}
}
else if (inputs[i].isMouseMotion) {
mouseHoldCount[i]++; // input remains pressed, increment mouse hold
}
}
else if (!inputs[i].pressed) {
if (inputs[i].bufferSum > pressThreshold) { // input becomes pressed
inputChanged = true;
inputs[i].pressed = true;
if (inputs[i].isKey) {
Keyboard.press(inputs[i].keyCode);
}
}
}
}
#ifdef DEBUG3
if (inputChanged) {
Serial.println("change");
}
#endif
}
/*
///////////////////////////
// SEND KEY EVENTS (obsolete, used in versions with pro micro bootloader)
///////////////////////////
void sendKeyEvents() {
if (inputChanged) {
KeyReport report = {
0 };
for (int i=0; i<6; i++) {
report.keys[i] = 0;
}
int count = 0;
for (int i=0; i<NUM_INPUTS; i++) {
if (inputs[i].pressed && (count < 6)) {
report.keys[count] = inputs[i].keyCode;
#ifdef DEBUG3
Serial.println(report.keys[count]);
#endif
count++;
}
}
if (count > 0) {
report.modifiers = 0x00;
report.reserved = 1;
Keyboard.sendReport(&report);
}
else {
report.modifiers = 0x00;
report.reserved = 0;
Keyboard.sendReport(&report);
}
}
else {
// might need a delay here to compensate for the time it takes to send keyreport
}
}
*/
/////////////////////////////
// SEND MOUSE BUTTON EVENTS
/////////////////////////////
void sendMouseButtonEvents() {
if (inputChanged) {
for (int i=0; i<NUM_INPUTS; i++) {
if (inputs[i].isMouseButton) {
if (inputs[i].pressed) {
if (inputs[i].keyCode == MOUSE_LEFT) {
Mouse.press(MOUSE_LEFT);
}
if (inputs[i].keyCode == MOUSE_RIGHT) {
Mouse.press(MOUSE_RIGHT);
}
}
else if (inputs[i].prevPressed) {
if (inputs[i].keyCode == MOUSE_LEFT) {
Mouse.release(MOUSE_LEFT);
}
if (inputs[i].keyCode == MOUSE_RIGHT) {
Mouse.release(MOUSE_RIGHT);
}
}
}
}
}
}
//////////////////////////////
// SEND MOUSE MOVEMENT EVENTS
//////////////////////////////
void sendMouseMovementEvents() {
byte right = 0;
byte left = 0;
byte down = 0;
byte up = 0;
int horizmotion = 0;
int vertmotion = 0;
mouseMovementCounter++;
mouseMovementCounter %= MOUSE_MOTION_UPDATE_INTERVAL;
if (mouseMovementCounter == 0) {
for (int i=0; i<NUM_INPUTS; i++) {
#ifdef DEBUG_MOUSE
// Serial.println(inputs[i].isMouseMotion);
#endif
if (inputs[i].isMouseMotion) {
if (inputs[i].pressed) {
if (inputs[i].keyCode == MOUSE_MOVE_UP) {
// JL Changes (x4): now update to 1 + a hold factor, constrained between 1 and mouse max movement speed
up=constrain(1+mouseHoldCount[i]/MOUSE_RAMP_SCALE, 1, MOUSE_MAX_PIXELS);
}
if (inputs[i].keyCode == MOUSE_MOVE_DOWN) {
down=constrain(1+mouseHoldCount[i]/MOUSE_RAMP_SCALE, 1, MOUSE_MAX_PIXELS);
}
if (inputs[i].keyCode == MOUSE_MOVE_LEFT) {
left=constrain(1+mouseHoldCount[i]/MOUSE_RAMP_SCALE, 1, MOUSE_MAX_PIXELS);
}
if (inputs[i].keyCode == MOUSE_MOVE_RIGHT) {
right=constrain(1+mouseHoldCount[i]/MOUSE_RAMP_SCALE, 1, MOUSE_MAX_PIXELS);
}
}
}
}
// diagonal scrolling and left/right cancellation
if(left > 0)
{
if(right > 0)
{
horizmotion = 0; // cancel horizontal motion because left and right are both pushed
}
else
{
horizmotion = -left; // left yes, right no
}
}
else
{
if(right > 0)
{
horizmotion = right; // right yes, left no
}
}
if(down > 0)
{
if(up > 0)
{
vertmotion = 0; // cancel vertical motion because up and down are both pushed
}
else
{
vertmotion = down; // down yes, up no
}
}
else
{
if (up > 0)
{
vertmotion = -up; // up yes, down no
}
}
// now move the mouse
if( !((horizmotion == 0) && (vertmotion==0)) )
{
Mouse.move(horizmotion * PIXELS_PER_MOUSE_STEP, vertmotion * PIXELS_PER_MOUSE_STEP);
}
}
}
///////////////////////////
// ADD DELAY
///////////////////////////
void addDelay() {
loopTime = micros() - prevTime;
if (loopTime < TARGET_LOOP_TIME) {
int wait = TARGET_LOOP_TIME - loopTime;
delayMicroseconds(wait);
}
prevTime = micros();
#ifdef DEBUG_TIMING
if (loopCounter == 0) {
int t = micros()-prevTime;
Serial.println(t);
}
loopCounter++;
loopCounter %= 999;
#endif
}
///////////////////////////
// CYCLE LEDS
///////////////////////////
void cycleLEDs() {
pinMode(inputLED_a, INPUT);
pinMode(inputLED_b, INPUT);
pinMode(inputLED_c, INPUT);
digitalWrite(inputLED_a, LOW);
digitalWrite(inputLED_b, LOW);
digitalWrite(inputLED_c, LOW);
ledCycleCounter++;
ledCycleCounter %= 6;
if ((ledCycleCounter == 0) && inputs[0].pressed) {
pinMode(inputLED_a, INPUT);
digitalWrite(inputLED_a, LOW);
pinMode(inputLED_b, OUTPUT);
digitalWrite(inputLED_b, HIGH);
pinMode(inputLED_c, OUTPUT);
digitalWrite(inputLED_c, LOW);
}
if ((ledCycleCounter == 1) && inputs[1].pressed) {
pinMode(inputLED_a, OUTPUT);
digitalWrite(inputLED_a, HIGH);
pinMode(inputLED_b, OUTPUT);
digitalWrite(inputLED_b, LOW);
pinMode(inputLED_c, INPUT);
digitalWrite(inputLED_c, LOW);
}
if ((ledCycleCounter == 2) && inputs[2].pressed) {
pinMode(inputLED_a, OUTPUT);
digitalWrite(inputLED_a, LOW);
pinMode(inputLED_b, OUTPUT);
digitalWrite(inputLED_b, HIGH);
pinMode(inputLED_c, INPUT);
digitalWrite(inputLED_c, LOW);
}
if ((ledCycleCounter == 3) && inputs[3].pressed) {
pinMode(inputLED_a, INPUT);
digitalWrite(inputLED_a, LOW);
pinMode(inputLED_b, OUTPUT);
digitalWrite(inputLED_b, LOW);
pinMode(inputLED_c, OUTPUT);
digitalWrite(inputLED_c, HIGH);
}
if ((ledCycleCounter == 4) && inputs[4].pressed) {
pinMode(inputLED_a, OUTPUT);
digitalWrite(inputLED_a, LOW);
pinMode(inputLED_b, INPUT);
digitalWrite(inputLED_b, LOW);
pinMode(inputLED_c, OUTPUT);
digitalWrite(inputLED_c, HIGH);
}
if ((ledCycleCounter == 5) && inputs[5].pressed) {
pinMode(inputLED_a, OUTPUT);
digitalWrite(inputLED_a, HIGH);
pinMode(inputLED_b, INPUT);
digitalWrite(inputLED_b, LOW);
pinMode(inputLED_c, OUTPUT);
digitalWrite(inputLED_c, LOW);
}
}
///////////////////////////
// DANCE LEDS
///////////////////////////
void danceLeds()
{
int delayTime = 50;
int delayTime2 = 100;
// CIRCLE
for(int i=0; i<4; i++)
{
// UP
pinMode(inputLED_a, INPUT);
digitalWrite(inputLED_a, LOW);
pinMode(inputLED_b, OUTPUT);
digitalWrite(inputLED_b, HIGH);
pinMode(inputLED_c, OUTPUT);
digitalWrite(inputLED_c, LOW);
delay(delayTime);
//RIGHT
pinMode(inputLED_a, INPUT);
digitalWrite(inputLED_a, LOW);
pinMode(inputLED_b, OUTPUT);
digitalWrite(inputLED_b, LOW);
pinMode(inputLED_c, OUTPUT);
digitalWrite(inputLED_c, HIGH);
delay(delayTime);
// DOWN
pinMode(inputLED_a, OUTPUT);
digitalWrite(inputLED_a, HIGH);
pinMode(inputLED_b, OUTPUT);
digitalWrite(inputLED_b, LOW);
pinMode(inputLED_c, INPUT);
digitalWrite(inputLED_c, LOW);
delay(delayTime);
// LEFT
pinMode(inputLED_a, OUTPUT);
digitalWrite(inputLED_a, LOW);
pinMode(inputLED_b, OUTPUT);
digitalWrite(inputLED_b, HIGH);
pinMode(inputLED_c, INPUT);
digitalWrite(inputLED_c, LOW);
delay(delayTime);
}
// WIGGLE
for(int i=0; i<4; i++)
{
// SPACE
pinMode(inputLED_a, OUTPUT);
digitalWrite(inputLED_a, HIGH);
pinMode(inputLED_b, INPUT);
digitalWrite(inputLED_b, LOW);
pinMode(inputLED_c, OUTPUT);
digitalWrite(inputLED_c, LOW);
delay(delayTime2);
// CLICK
pinMode(inputLED_a, OUTPUT);
digitalWrite(inputLED_a, LOW);
pinMode(inputLED_b, INPUT);
digitalWrite(inputLED_b, LOW);
pinMode(inputLED_c, OUTPUT);
digitalWrite(inputLED_c, HIGH);
delay(delayTime2);
}
}
void updateOutLEDs()
{
boolean keyPressed = 0;
boolean mousePressed = 0;
for (int i=0; i<NUM_INPUTS; i++)
{
if (inputs[i].pressed)
{
if (inputs[i].isKey)
{
keyPressed = 1;
#ifdef DEBUG
Serial.print("Key ");
Serial.print(i);
Serial.println(" pressed");
#endif
}
else
{
mousePressed = 1;
}
}
}
if (keyPressed)
{
digitalWrite(outputK, HIGH);
//TXLED1;
}
else
{
digitalWrite(outputK, LOW);
//TXLED0;
}
if (mousePressed)
{
digitalWrite(outputM, HIGH);
//RXLED1;
}
else
{
digitalWrite(outputM, LOW);
//RXLED0;
}
}
#include "Arduino.h"
/*
/////////////////////////////////////////////////////////////////////////
// KEY MAPPINGS: WHICH KEY MAPS TO WHICH PIN ON THE Michi BOARD? //
/////////////////////////////////////////////////////////////////////////
- edit the keyCodes array below to change the keys sent by the Michi for each input
- the comments tell you which input sends that key (for example, by default 'w' is sent by pin D5)
- change the keys by replacing them. for example, you can replace 'w' with any other individual letter,
number, or symbol on your keyboard
- you can also use codes for other keys such as modifier and function keys (see the
the list of additional key codes at the bottom of this file)
*/
int keyCodes[NUM_INPUTS] = {
// top side of the michi board
KEY_UP_ARROW, // up arrow pad
KEY_DOWN_ARROW, // down arrow pad
KEY_LEFT_ARROW, // left arrow pad
KEY_RIGHT_ARROW, // right arrow pad
' ', // space button pad
MOUSE_LEFT, // click button pad
// female header on the back left side
'w', // pin D5
'a', // pin D4
's', // pin D3
'd', // pin D2
};
///////////////////////////
// NOISE CANCELLATION /////
///////////////////////////
#define SWITCH_THRESHOLD_OFFSET_PERC 5 // number between 1 and 49
// larger value protects better against noise oscillations, but makes it harder to press and release
// recommended values are between 2 and 20
// default value is 5
#define SWITCH_THRESHOLD_CENTER_BIAS 55 // number between 1 and 99
// larger value makes it easier to "release" keys, but harder to "press"
// smaller value makes it easier to "press" keys, but harder to "release"
// recommended values are between 30 and 70
// 50 is "middle" 2.5 volt center
// default value is 55
// 100 = 5V (never use this high)
// 0 = 0 V (never use this low
/////////////////////////
// MOUSE MOTION /////////
/////////////////////////
#define MOUSE_MOTION_UPDATE_INTERVAL 35 // how many loops to wait between
// sending mouse motion updates
#define PIXELS_PER_MOUSE_STEP 4 // a larger number will make the mouse
// move faster
#define MOUSE_RAMP_SCALE 150 // Scaling factor for mouse movement ramping
// Lower = more sensitive mouse movement
// Higher = slower ramping of speed
// 0 = Ramping off
#define MOUSE_MAX_PIXELS 10 // Max pixels per step for mouse movement
/*
///////////////////////////
// ADDITIONAL KEY CODES ///
///////////////////////////
- you can use these codes in the keyCodes array above
- to get modifier keys, function keys, etc
KEY_LEFT_CTRL
KEY_LEFT_SHIFT
KEY_LEFT_ALT
KEY_LEFT_GUI
KEY_RIGHT_CTRL
KEY_RIGHT_SHIFT
KEY_RIGHT_ALT
KEY_RIGHT_GUI
KEY_BACKSPACE
KEY_TAB
KEY_RETURN
KEY_ESC
KEY_INSERT
KEY_DELETE
KEY_PAGE_UP
KEY_PAGE_DOWN
KEY_HOME
KEY_END
KEY_CAPS_LOCK
KEY_F1
KEY_F2
KEY_F3
KEY_F4
KEY_F5
KEY_F6
KEY_F7
KEY_F8
KEY_F9
KEY_F10
KEY_F11
KEY_F12
*/
Comments
Please log in or sign up to comment.