Storing images or image data on SD (gaming)

Status
Not open for further replies.
ok heres the deal. I'm working with two libraries, the t3 library and esp32 library. As it stands, testing both libraries requires me to switch out the MCU every other time I need to test something. Major PITA!!! So I wanted to have a console for each library. Unfortunately I cant get any support from Adafruit and the esp32 forum hasn't been much help either.

This is what I have. I went in to the pins_arduino (pin out library) and marked out the DAC pin assignments and used those pins as the second set of i2c pins.

Code:
static const uint8_t TX = 17;
static const uint8_t RX = 16;

static const uint8_t SDA = 23;
static const uint8_t SCL = 22;

static const uint8_t SDA2 = 25;
static const uint8_t SCL2 = 26;

static const uint8_t SS    = 2;
static const uint8_t MOSI  = 18;
static const uint8_t MISO  = 19;
static const uint8_t SCK   = 5;

// mapping to match other feathers and also in order
static const uint8_t A0 = 26; 
static const uint8_t A1 = 25;
static const uint8_t A2 = 34;
static const uint8_t A3 = 39;
static const uint8_t A4 = 36;
static const uint8_t A5 = 4;
static const uint8_t A6 = 14;
static const uint8_t A7 = 32;
static const uint8_t A8 = 15;
static const uint8_t A9 = 33;
static const uint8_t A10 = 27;
static const uint8_t A11 = 12;
static const uint8_t A12 = 13;

// vbat measure
static const uint8_t A13 = 35;
//static const uint8_t Ax = 0; // not used/available
//static const uint8_t Ax = 2; // not used/available


static const uint8_t T0 = 4;
static const uint8_t T1 = 0;
static const uint8_t T2 = 2;
static const uint8_t T3 = 15;
static const uint8_t T4 = 13;
static const uint8_t T5 = 12;
static const uint8_t T6 = 14;
static const uint8_t T7 = 27;
static const uint8_t T8 = 33;
static const uint8_t T9 = 32;

//static const uint8_t DAC1 = 25;
//static const uint8_t DAC2 = 26;

#endif /* Pins_Arduino_h */

then I was told I needed to add this to the wire.cpp file.

TwoWire Wire = TwoWire(0);
TwoWire Wire2 = TwoWire(0);

Then I added the second address for the second i2c device like so..

#define SEESAW_ADDRESS (0x49)
#define SEESAW_ADDRESS2 (0x4a)

but I cant get an answer from any one on what to do next.

I feel like I should add the second i2c lines to wire .h like so....

Code:
class TwoWire: public Stream
{
protected:
    uint8_t num;
    int8_t sda;
    int8_t scl;
    int8_t sda2;    //////added by duhjoker
    int8_t scl2;    //////"              "

    i2c_t * i2c;

    uint8_t rxBuffer[I2C_BUFFER_LENGTH];
    uint16_t rxIndex;
    uint16_t rxLength;

    uint8_t txBuffer[I2C_BUFFER_LENGTH];
    uint16_t txIndex;
    uint16_t txLength;
    uint8_t txAddress;

    uint8_t transmitting;

then add the sda2 stuff to this......

Code:
void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
{
    if(sdaPin < 0) {
        if(num == 0) {
            sdaPin = SDA;
        } else {
            return;
        }
    }

    if(sclPin < 0) {
        if(num == 0) {
            sclPin = SCL;
        } else {
            return;
        }
    }

    if(i2c == NULL) {
        i2c = i2cInit(num, 0, false);
        if(i2c == NULL) {
            return;
        }
    }

    i2cSetFrequency(i2c, frequency);

    if(sda >= 0 && sda != sdaPin) {
        i2cDetachSDA(i2c, sda);
    }

    if(scl >= 0 && scl != sclPin) {
        i2cDetachSCL(i2c, scl);
    }

    sda = sdaPin;
    scl = sclPin;

    i2cAttachSDA(i2c, sda);
    i2cAttachSCL(i2c, scl);

    flush();

    i2cInitFix(i2c);
}


but like I said I cant get any answers.
 
also, you can run 2 IDEs independantly each pointed at their proper board definitions
its how i program /debug esp8266 & teensy 3.6 in my project
 
Most of the problems ive been fixing or updating here lately have been visual. Like a popup function that can be used to easily display text bubbles when certain conditions are met. Or the buttons library I just added to the grafx libraries that makes it easier to add button controls. Some code works for some MCU's but not others for example the buton libraries begin function had a call to disable the pull up resistor when not being used to save power. it worked for Arduino units just find but the teensy didn't like it at all.

Code:
/*
 * reads each button states and store it
 */
void GrafxT3::updateB() {
    for (uint8_t thisButton = 0; thisButton < NUM_BTN; thisButton++) {
        pinMode(pins[thisButton], INPUT_PULLUP); //enable internal pull up resistors
        if (digitalRead(pins[thisButton]) == LOW) { //if button pressed
            states[thisButton]++; //increase button hold time
        } else {
            if (states[thisButton] == 0)//button idle
                continue;
            if (states[thisButton] == 0xFF)//if previously released
                states[thisButton] = 0; //set to idle
            else
                states[thisButton] = 0xFF; //button just released
        }
//        pinMode(pins[thisButton], INPUT); //disable internal pull up resistors to save power
    }
	

}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Any way....

Ive been studying the seesaw and wire libraries. So seesaw uses a begin like so...

Code:
bool Adafruit_seesaw::begin(uint8_t addr)
{
	_i2caddr = addr;
	
	_i2c_init(); ///////////////////////

	SWReset();
	delay(500);

	uint8_t c = this->read8(SEESAW_STATUS_BASE, SEESAW_STATUS_HW_ID);

	if(c != SEESAW_HW_ID_CODE) return false;
	
	return true;
}

the commented line points to this function in the seesaw library
Code:
void Adafruit_seesaw::_i2c_init()
{
  Wire.begin();
}

So now lets look at wire begin...
Code:
void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
{
    if(sdaPin < 0) {
        if(num == 0) {
            sdaPin = SDA;
        } else {
            return;
        }
    }

    if(sclPin < 0) {
        if(num == 0) {
            sclPin = SCL;
        } else {
            return;
        }
    }

    if(i2c == NULL) {
        i2c = i2cInit(num, 0, false);
        if(i2c == NULL) {
            return;
        }
    }

    i2cSetFrequency(i2c, frequency);

    if(sda >= 0 && sda != sdaPin) {
        i2cDetachSDA(i2c, sda);
    }

    if(scl >= 0 && scl != sclPin) {
        i2cDetachSCL(i2c, scl);
    }

    sda = sdaPin;
    scl = sclPin;

    i2cAttachSDA(i2c, sda);
    i2cAttachSCL(i2c, scl);

    flush();

    i2cInitFix(i2c);
}

it a long way to go around but why not just add a second begin to wire that points to sda2 instead of sda then do the same thing with _i2c_init() then I can call begin in the sketch twice. once for address 1 and the second for address 2

or something like this..

Code:
bool Adafruit_seesaw::begin(uint8_t addr1, uint8_t addr2)
{
	_i2caddr = addr1;
        _i2caddr = addr2;
	
	_i2c_init1();
        _i2c_init2();


	SWReset();
	delay(500);

	uint8_t c = this->read8(SEESAW_STATUS_BASE, SEESAW_STATUS_HW_ID);

	if(c != SEESAW_HW_ID_CODE) return false;
	
	return true;
}
 
ok it compiles but its untested heres what I did...

wire.h
Code:
class TwoWire: public Stream
{
protected:
    uint8_t num;
    int8_t sda;
    int8_t scl;
    int8_t sda2;    //////added by duhjoker
    int8_t scl2;    //////"              "

    i2c_t * i2c;

    uint8_t rxBuffer[I2C_BUFFER_LENGTH];
    uint16_t rxIndex;
    uint16_t rxLength;

    uint8_t txBuffer[I2C_BUFFER_LENGTH];
    uint16_t txIndex;
    uint16_t txLength;
    uint8_t txAddress;

    uint8_t transmitting;

public:
    TwoWire(uint8_t bus_num);
    void begin(int sda=-1, int scl=-1, uint32_t frequency1=100000);
    void begin2(int sda2=-1, int scl2=-1, uint32_t frequency2=100000);

wire.cpp
Code:
void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency1)
{
    if(sdaPin < 0) {
        if(num == 0) {
            sdaPin = SDA;
        } else {
            return;
        }
    }

    if(sclPin < 0) {
        if(num == 0) {
            sclPin = SCL;
        } else {
            return;
        }
    }

    if(i2c == NULL) {
        i2c = i2cInit(num, 0, false);
        if(i2c == NULL) {
            return;
        }
    }

    i2cSetFrequency(i2c, frequency1);

    if(sda >= 0 && sda != sdaPin) {
        i2cDetachSDA(i2c, sda);
    }

    if(scl >= 0 && scl != sclPin) {
        i2cDetachSCL(i2c, scl);
    }

    sda = sdaPin;
    scl = sclPin;

    i2cAttachSDA(i2c, sda);
    i2cAttachSCL(i2c, scl);

    flush();

    i2cInitFix(i2c);
}

void TwoWire::begin2(int sdaPin, int sclPin, uint32_t frequency2)
{
    if(sdaPin < 0) {
        if(num == 0) {
            sdaPin = SDA2;
        } else {
            return;
        }
    }

    if(sclPin < 0) {
        if(num == 0) {
            sclPin = SCL2;
        } else {
            return;
        }
    }

    if(i2c == NULL) {
        i2c = i2cInit(num, 0, false);
        if(i2c == NULL) {
            return;
        }
    }

    i2cSetFrequency(i2c, frequency2);

    if(sda >= 0 && sda != sdaPin) {
        i2cDetachSDA(i2c, sda2);
    }

    if(scl >= 0 && scl != sclPin) {
        i2cDetachSCL(i2c, scl2);
    }

    sda2 = sdaPin;
    scl2 = sclPin;

    i2cAttachSDA(i2c, sda2);
    i2cAttachSCL(i2c, scl2);

    flush();

    i2cInitFix(i2c);
}

and seesaw cpp
Code:
bool Adafruit_seesaw::begin(uint8_t addr)
{
	_i2caddr = addr;
	
	_i2c_init();

	SWReset();
	delay(500);

	uint8_t c = this->read8(SEESAW_STATUS_BASE, SEESAW_STATUS_HW_ID);

	if(c != SEESAW_HW_ID_CODE) return false;
	
	return true;
}

bool Adafruit_seesaw::begin2(uint8_t addr2)
{
	_i2caddr = addr2;
	
	_i2c_init2();

	SWReset();
	delay(500);

	uint8_t c = this->read8(SEESAW_STATUS_BASE, SEESAW_STATUS_HW_ID);

	if(c != SEESAW_HW_ID_CODE) return false;
	
	return true;
}


I'm going to add the seesaw begins together
 
ok I did this

Code:
void Adafruit_seesaw::_i2c_init()
{
  Wire.begin();
  Wire.begin2();
}

and then this
Code:
bool Adafruit_seesaw::begin(uint8_t addr1, uint8_t addr2)
{
	_i2caddr = addr1;
	_i2caddr2 = addr2;

	_i2c_init();

	SWReset();
	delay(500);

	uint8_t c = this->read8(SEESAW_STATUS_BASE, SEESAW_STATUS_HW_ID);

	if(c != SEESAW_HW_ID_CODE) return false;
	
	return true;
}

now I'm wondering if I need to change every thing with address to address 1 and 2
 
Last edited:
duhjoker, you can share ~ 127 different addressed i2c devices (more if you use a multiplexer, but that deserves its own thread). i wouldnt worry about “reaction” times with human touch interfaced games, i was streaming fastled payloads of 500 bytes over the bus to a strip and it was running smooth. if you wanna go overkill you could hit up the spi bus, but for a human response you wont see much difference in your game

btw, the Wii nunchuck uses I2C

Tony

are saying just use both on one bus
 
are saying just use both on one bus

Up to you. You'll have to try it and see what works the best for you.

Here's what I used for both on separate busses:

Code:
#include "input.h"

Input controller;

void setup() {
    controller.begin();
}

void loop() {
    controller.update_buttons();
    controller.update_analog_x_axis();
    controller.update_analog_y_axis();

    // BUTTON_SEL is pressed
    // !(this button == 0) and (last button == 1)
    // (!(controller.buttons1 & (1<<BUTTON_SEL)) && (controller.buttons1_last & (1<<BUTTON_SEL)))

    // BUTTON_SEL is released
    // !(this button == 1) and (last button == 0)
    // ((controller.buttons1 & (1<<BUTTON_SEL)) && !(controller.buttons1_last & (1<<BUTTON_SEL)))

    // BUTTON_SEL is held
    if (!(controller.buttons1 & (1<<BUTTON_SEL)) || !(controller.buttons2 & (1<<BUTTON_SEL))) {
        // do something
    }
}

input.h

Code:
#ifndef INPUT_H
#define INPUT_H

#include <Arduino.h>
#include <i2c_t3.h>
#include "vec2.h"

enum {
  SEESAW_STATUS_BASE = 0x00,
  SEESAW_GPIO_BASE = 0x01,
  SEESAW_SERCOM0_BASE = 0x02,

  SEESAW_TIMER_BASE = 0x08,
  SEESAW_ADC_BASE = 0x09,
  SEESAW_DAC_BASE = 0x0A,
  SEESAW_INTERRUPT_BASE = 0x0B,
  SEESAW_DAP_BASE = 0x0C,
  SEESAW_EEPROM_BASE = 0x0D,
  SEESAW_NEOPIXEL_BASE = 0x0E,
};

// GPIO module function addres registers
enum {
  SEESAW_GPIO_DIRSET_BULK = 0x02,
  SEESAW_GPIO_DIRCLR_BULK = 0x03,
  SEESAW_GPIO_BULK = 0x04,
  SEESAW_GPIO_BULK_SET = 0x05,
  SEESAW_GPIO_BULK_CLR = 0x06,
  SEESAW_GPIO_BULK_TOGGLE = 0x07,
  SEESAW_GPIO_INTENSET = 0x08,
  SEESAW_GPIO_INTENCLR = 0x09,
  SEESAW_GPIO_INTFLAG = 0x0A,
  SEESAW_GPIO_PULLENSET = 0x0B,
  SEESAW_GPIO_PULLENCLR = 0x0C,
};

enum {
  SEESAW_STATUS_HW_ID = 0x01,
  SEESAW_STATUS_VERSION = 0x02,
  SEESAW_STATUS_OPTIONS = 0x03,
  SEESAW_STATUS_SWRST = 0x7F,
};

enum {
  SEESAW_TIMER_STATUS = 0x00,
  SEESAW_TIMER_PWM = 0x01,
  SEESAW_TIMER_FREQ = 0x02,
};

enum {
  SEESAW_ADC_STATUS = 0x00,
  SEESAW_ADC_INTEN = 0x02,
  SEESAW_ADC_INTENCLR = 0x03,
  SEESAW_ADC_WINMODE = 0x04,
  SEESAW_ADC_WINTHRESH = 0x05,
  SEESAW_ADC_CHANNEL_OFFSET = 0x07,
};

enum {
  SEESAW_SERCOM_STATUS = 0x00,
  SEESAW_SERCOM_INTEN = 0x02,
  SEESAW_SERCOM_INTENCLR = 0x03,
  SEESAW_SERCOM_BAUD = 0x04,
  SEESAW_SERCOM_DATA = 0x05,
};

enum {
  SEESAW_NEOPIXEL_STATUS = 0x00,
  SEESAW_NEOPIXEL_PIN = 0x01,
  SEESAW_NEOPIXEL_SPEED = 0x02,
  SEESAW_NEOPIXEL_BUF_LENGTH = 0x03,
  SEESAW_NEOPIXEL_BUF = 0x04,
  SEESAW_NEOPIXEL_SHOW = 0x05,
};

#define ADC_INPUT_0_PIN 2
#define ADC_INPUT_1_PIN 3
#define ADC_INPUT_2_PIN 4
#define ADC_INPUT_3_PIN 5

#define PWM_0_PIN 4
#define PWM_1_PIN 5
#define PWM_2_PIN 6
#define PWM_3_PIN 7

#define SEESAW_HW_ID_CODE 0x55
#define SEESAW_EEPROM_I2C_ADDR 0x3F

#define JOY1_ADDRESS 0x49
#define JOY2_ADDRESS 0x4A

#define JOY1_IRQ 30
#define JOY2_IRQ 1

#define BUTTON_RIGHT 6
#define BUTTON_DOWN  7
#define BUTTON_LEFT  9
#define BUTTON_UP    10
#define BUTTON_SEL   14
// uint32_t button_mask = (1 << BUTTON_RIGHT) | (1 << BUTTON_DOWN) | (1 << BUTTON_LEFT) | (1 << BUTTON_UP) | (1 << BUTTON_SEL);

class Input {
 public:
  const uint32_t button_mask = 0b100011011000000;
  volatile uint32_t buttons1 = 0;
  volatile uint32_t buttons2 = 0;
  volatile uint32_t buttons1_last = 0;
  volatile uint32_t buttons2_last = 0;
  volatile uint16_t analog1x, analog1y, analog2x, analog2y;
  vec2f joystick1;
  vec2f joystick2;

  void begin();
  void set_GPIOInterrupts(uint8_t joy_address, uint8_t enabled);
  void set_pinModeBulk(uint8_t joy_address, uint8_t mode);
  void get_analog_sync(uint8_t adc_channel);
  void get_buttons_sync();
  void update_analog_x_axis();
  void update_analog_y_axis();
  void update_buttons();
};

#endif

input.cpp

Code:
#include "input.h"

void Input::begin() {
  Wire1.begin(I2C_MASTER, 0x00, I2C_PINS_37_38, I2C_PULLUP_EXT, 400000, I2C_OP_MODE_DMA);
  // Wire1.setOpMode(I2C_OP_MODE_ISR);
  if(Wire1.i2c->opMode == I2C_OP_MODE_DMA)
    Serial.printf("OK (Channel %d)\n",Wire1.i2c->DMA->channel);
  else
    Serial.print("Failed, using ISR\n");
  Wire1.setClock(1e6);
  Wire1.begin(JOY1_ADDRESS);

  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000, I2C_OP_MODE_DMA);
  // Wire.setOpMode(I2C_OP_MODE_ISR);
  if(Wire.i2c->opMode == I2C_OP_MODE_DMA)
    Serial.printf("OK (Channel %d)\n",Wire.i2c->DMA->channel);
  else
    Serial.print("Failed, using ISR\n");
  Wire.setClock(1e6);
  Wire.begin(JOY2_ADDRESS);

  // Wire.onTransmitDone(transmitDone);
  // Wire.onReqFromDone(requestDone);
  set_pinModeBulk(JOY1_ADDRESS, INPUT_PULLUP);
  set_pinModeBulk(JOY2_ADDRESS, INPUT_PULLUP);

  set_GPIOInterrupts(JOY1_ADDRESS, 1);
  set_GPIOInterrupts(JOY2_ADDRESS, 1);
}

void Input::set_GPIOInterrupts(uint8_t joy_address, uint8_t enabled) {
	uint8_t cmd[] = { (uint8_t)(button_mask >> 24), (uint8_t)(button_mask >> 16),
                    (uint8_t)(button_mask >> 8), (uint8_t)button_mask };
  auto w = Wire;
  if (joy_address == JOY1_ADDRESS)
    w = Wire1;
  w.beginTransmission(joy_address);
  w.write(SEESAW_GPIO_BASE);
  if(enabled)
    w.write(SEESAW_GPIO_INTENSET);
  else
    w.write(SEESAW_GPIO_INTENCLR);
  w.write(cmd, 4);
  w.endTransmission(I2C_NOSTOP);
}

void Input::set_pinModeBulk(uint8_t joy_address, uint8_t mode) {
  uint8_t cmd[] = { (uint8_t)(button_mask >> 24), (uint8_t)(button_mask >> 16),
                    (uint8_t)(button_mask >> 8), (uint8_t)button_mask };
  auto w = Wire;
  if (joy_address == JOY1_ADDRESS)
    w = Wire1;
  w.beginTransmission(joy_address);
  w.write(SEESAW_GPIO_BASE);
  w.write(SEESAW_GPIO_DIRCLR_BULK);
  w.write(cmd, 4);
  w.endTransmission(I2C_NOSTOP);
  w.beginTransmission(joy_address);
  w.write(SEESAW_GPIO_BASE);
  w.write(SEESAW_GPIO_PULLENSET);
  w.write(cmd, 4);
  w.endTransmission(I2C_NOSTOP);
  w.beginTransmission(joy_address);
  w.write(SEESAW_GPIO_BASE);
  w.write(SEESAW_GPIO_BULK_SET);
  w.write(cmd, 4);
  w.endTransmission();
}

void Input::update_buttons() {
  buttons1_last = buttons1;
  buttons2_last = buttons2;
  get_buttons_sync();
  delayMicroseconds(1000);
}

void Input::update_analog_x_axis() {
  get_analog_sync(1);
  joystick2.set(((float) analog2x), ((float) analog2y));
  joystick2 /= 1024;
  joystick2 -= 0.5;
  joystick2.x *= -1.0;
}

void Input::update_analog_y_axis() {
  get_analog_sync(0);
  joystick1.set(((float) analog1x), ((float) analog1y));
  joystick1 /= 1024;
  joystick1 -= 0.5;
  joystick1.y *= -1.0;
}

void Input::get_analog_sync(uint8_t adc_channel) {
  Wire1.beginTransmission(JOY1_ADDRESS);
  Wire1.write(SEESAW_ADC_BASE);
  // pin2 x-axis / pin3 y-axis
  Wire1.write(SEESAW_ADC_CHANNEL_OFFSET + adc_channel);
  Wire1.sendTransmission(I2C_STOP);  // non-blocking

  Wire.beginTransmission(JOY2_ADDRESS);
  Wire.write(SEESAW_ADC_BASE);
  // pin2 x-axis / pin3 y-axis
  Wire.write(SEESAW_ADC_CHANNEL_OFFSET + adc_channel);
  Wire.sendTransmission(I2C_STOP);  // non-blocking

  delayMicroseconds(1000);
  while (!Wire1.done() && !Wire.done()) {
    // todo: do something useful here
  }

  Wire1.sendRequest(JOY1_ADDRESS, 2, I2C_STOP);
  Wire.sendRequest(JOY2_ADDRESS, 2, I2C_STOP);

  delayMicroseconds(500);
  while (!Wire1.done() && !Wire.done()) {
    // TODO: maybe do something useful here
  }

  uint8_t buffer1[2];
  buffer1[0] = Wire1.read();
  buffer1[1] = Wire1.read();
  uint8_t buffer2[2];
  buffer2[0] = Wire.read();
  buffer2[1] = Wire.read();

  if (adc_channel == 0) {
    analog1x = ((uint16_t)buffer1[0] << 8) | (uint16_t)buffer1[1];
    analog2x = ((uint16_t)buffer2[0] << 8) | (uint16_t)buffer2[1];
  }
  else {
    analog1y = ((uint16_t)buffer1[0] << 8) | (uint16_t)buffer1[1];
    analog2y = ((uint16_t)buffer2[0] << 8) | (uint16_t)buffer2[1];
  }
  delay(1);
}

void Input::get_buttons_sync() {
  Wire1.beginTransmission(JOY1_ADDRESS);
  Wire1.write(SEESAW_GPIO_BASE);
  Wire1.write(SEESAW_GPIO_BULK);
  Wire1.sendTransmission(I2C_STOP);

  Wire.beginTransmission(JOY2_ADDRESS);
  Wire.write(SEESAW_GPIO_BASE);
  Wire.write(SEESAW_GPIO_BULK);
  Wire.sendTransmission(I2C_STOP);

  // needed for seesaw to get the data
  delayMicroseconds(375);
  while (!Wire1.done() && !Wire.done()) {
  }

  Wire1.sendRequest(JOY1_ADDRESS, 4, I2C_STOP);
  Wire.sendRequest(JOY2_ADDRESS, 4, I2C_STOP);

  delayMicroseconds(500);
  while (!Wire1.done() && !Wire.done()) {
  }

  uint8_t buffer1[4];
  buffer1[0] = Wire1.read();
  buffer1[1] = Wire1.read();
  buffer1[2] = Wire1.read();
  buffer1[3] = Wire1.read();
  buttons1 = (((uint32_t)buffer1[0] << 24) | ((uint32_t)buffer1[1] << 16) |
              ((uint32_t)buffer1[2] << 8) | (uint32_t)buffer1[3]) & button_mask;

  uint8_t buffer2[4];
  buffer2[0] = Wire.read();
  buffer2[1] = Wire.read();
  buffer2[2] = Wire.read();
  buffer2[3] = Wire.read();
  buttons2 = (((uint32_t)buffer2[0] << 24) | ((uint32_t)buffer2[1] << 16) |
              ((uint32_t)buffer2[2] << 8) | (uint32_t)buffer2[3]) & button_mask;
}

vec2.h
source: https://gist.github.com/acidleaf/8957147

Code:
#ifndef __VEC2_H__
#define __VEC2_H__

#include <Arduino.h>

template <class T>
class vec2 {
public:
	T x, y;

	vec2() :x(0), y(0) {}
	vec2(T x, T y) : x(x), y(y) {}
	vec2(const vec2& v) : x(v.x), y(v.y) {}

	vec2& operator=(const vec2& v) {
		x = v.x;
		y = v.y;
		return *this;
	}

	vec2 operator+(vec2& v) {
		return vec2(x + v.x, y + v.y);
	}
	vec2 operator-(vec2& v) {
		return vec2(x - v.x, y - v.y);
	}

	vec2& operator+=(vec2& v) {
		x += v.x;
		y += v.y;
		return *this;
	}
	vec2& operator-=(vec2& v) {
		x -= v.x;
		y -= v.y;
		return *this;
	}

	vec2 operator+(double s) {
		return vec2(x + s, y + s);
	}
	vec2 operator-(double s) {
		return vec2(x - s, y - s);
	}
	vec2 operator*(double s) {
		return vec2(x * s, y * s);
	}
	vec2 operator/(double s) {
		return vec2(x / s, y / s);
	}


	vec2& operator+=(double s) {
		x += s;
		y += s;
		return *this;
	}
	vec2& operator-=(double s) {
		x -= s;
		y -= s;
		return *this;
	}
	vec2& operator*=(double s) {
		x *= s;
		y *= s;
		return *this;
	}
	vec2& operator/=(double s) {
		x /= s;
		y /= s;
		return *this;
	}

	void set(T x, T y) {
		this->x = x;
		this->y = y;
	}

	void rotate(double theta) {
		/* double theta = deg / 180.0 * M_PI; */
		double c = cos(theta);
		double s = sin(theta);
		double tx = x * c - y * s;
		double ty = x * s + y * c;
		x = tx;
		y = ty;
	}

	vec2& normalize() {
		double len = length();
		if (len == 0) return *this;
		*this *= (1.0 / len);
		return *this;
	}

	float dist(vec2 v) const {
		vec2 d(v.x - x, v.y - y);
		return d.length();
	}
	float length() const {
		return sqrt(x * x + y * y);
	}

	float angle() const {
		return atan2f(y, x);
	}

	void truncate(double length) {
		double angle = atan2f(y, x);
		x = length * cos(angle);
		y = length * sin(angle);
	}

	vec2 ortho() const {
		return vec2(y, -x);
	}
	vec2 perpendicular() const {
		return vec2(-y, x);
	}

	static float dot(vec2 v1, vec2 v2) {
		return v1.x * v2.x + v1.y * v2.y;
	}
	static float cross(vec2 v1, vec2 v2) {
		return (v1.x * v2.y) - (v1.y * v2.x);
	}

};

typedef vec2<float> vec2f;
typedef vec2<double> vec2d;

#endif

Edit: this uses the https://github.com/nox771/i2c_t3 library which is teensy specific
 
Last edited:
looking at the library, attaching 2 seesaws to wire port you construct 2 objects

Adafruit_seesaw seesaw1;
Adafruit_seesaw seesaw2;

then you enable them.

seesaw1.begin(0x49);
seesaw2.begin(0x4A);

use the object you want to control after using its functions:

seesaw2.whatever()
 
its not hard to make it work with ANY bus, not just 1 or 2, how about the 4 on teensy 3.6?. point is, if adafruit took a few extra minutes they could of added the TwoWire pointer to their class constructor which would have worked on any. given port.

Adafruit_seesaw seesaw1 = Adafruit_seesaw(&Wire2);
 
I decided to go ahead and add the interrupt lines so the button states can be read. what pin capabilities are needed to use the interrupts.
 
all digital pins have interrupt capability, you can use the bounce library to prevent mechanical chatter from your buttons when pressing
 
see that's where I get confused. The adafruit page calls them all gpio and analog pins. Does that mean they can be digital I/O or analog?

heres the pinout from pins_arduino. iy list some as analog but lower down it also gives them a T number. what are the T numbers representing?

Code:
#define NUM_DIGITAL_PINS        40
#define NUM_ANALOG_INPUTS       16

#define analogInputToDigitalPin(p)  (((p)<20)?(esp32_adc2gpio[(p)]):-1)
#define digitalPinToInterrupt(p)    (((p)<40)?(p):-1)
#define digitalPinHasPWM(p)         (p < 34)

static const uint8_t LED_BUILTIN = 13;
#define BUILTIN_LED  LED_BUILTIN // backward compatibility

static const uint8_t TX = 17;
static const uint8_t RX = 16;

static const uint8_t SDA = 23;
static const uint8_t SCL = 22;

static const uint8_t SS    = 2;
static const uint8_t MOSI  = 18;
static const uint8_t MISO  = 19;
static const uint8_t SCK   = 5;

// mapping to match other feathers and also in order
static const uint8_t A0 = 26; 
static const uint8_t A1 = 25;
static const uint8_t A2 = 34;
static const uint8_t A3 = 39;
static const uint8_t A4 = 36;
static const uint8_t A5 = 4;
static const uint8_t A6 = 14;
static const uint8_t A7 = 32;
static const uint8_t A8 = 15;
static const uint8_t A9 = 33;
static const uint8_t A10 = 27;
static const uint8_t A11 = 12;
static const uint8_t A12 = 13;

// vbat measure
static const uint8_t A13 = 35;
//static const uint8_t Ax = 0; // not used/available
//static const uint8_t Ax = 2; // not used/available


static const uint8_t T0 = 4;
static const uint8_t T1 = 0;
static const uint8_t T2 = 2;
static const uint8_t T3 = 15;
static const uint8_t T4 = 13;
static const uint8_t T5 = 12;
static const uint8_t T6 = 14;
static const uint8_t T7 = 27;
static const uint8_t T8 = 33;
static const uint8_t T9 = 32;

static const uint8_t DAC1 = 25;
static const uint8_t DAC2 = 26;
 
Ok only using one i2c bus means I only I had to change one function in the seesaw file. Which would be the begin. the change is to allow the second address.

Code:
 #define SEESAW_ADDRESS                (0x49)
    #define SEESAW_ADDRESS2               (0x4a)

bool Adafruit_seesaw::begin(uint8_t addr1, uint8_t addr2)
{
	_i2caddr = addr1;

	_i2caddr2 = addr2;

	_i2c_init();

	SWReset();
	delay(500);

	uint8_t c = this->read8(SEESAW_STATUS_BASE, SEESAW_STATUS_HW_ID);

	if(c != SEESAW_HW_ID_CODE) return false;
	
	return true;
}


Now when begin is it should initialize both 0x49 and 0x4a.

On to the sketch modification. First what are these pin numbers for if the i2c takes care of all the hardware installed on the board.
Code:
#include "Adafruit_seesaw.h"
Adafruit_seesaw ss;
#define BUTTON_RIGHT 6  /////////////what are these integers
#define BUTTON_DOWN  7  /////////////"                      "
#define BUTTON_LEFT  9  /////////////"                      "
#define BUTTON_UP    10 /////////////"                      "
#define BUTTON_SEL   14 /////////////"                      "
uint32_t button_mask = (1 << BUTTON_RIGHT) | (1 << BUTTON_DOWN) | 
                (1 << BUTTON_LEFT) | (1 << BUTTON_UP) | (1 << BUTTON_SEL);
#if defined(ESP8266)
  #define IRQ_PIN   2
#elif defined(ESP32)
  #define IRQ_PIN   14
#elif defined(NRF52)
  #define IRQ_PIN   27
#elif defined(TEENSYDUINO)
  #define IRQ_PIN   8
#elif defined(ARDUINO_ARCH_WICED)
  #define IRQ_PIN   PC5
#else
  #define IRQ_PIN   5
#endif
void setup() {
  Serial.begin(115200);
  while(!Serial);
  
  if(!ss.begin(0x49)){
    Serial.println("ERROR!");
    while(1);
  }
  else{
    Serial.println("seesaw started");
    Serial.print("version: ");
    Serial.println(ss.getVersion(), HEX);
  }
  ss.pinModeBulk(button_mask, INPUT_PULLUP);
  ss.setGPIOInterrupts(button_mask, 1);
  pinMode(IRQ_PIN, INPUT);
}
int last_x = 0, last_y = 0;
void loop() {
  int x = ss.analogRead(2);
  int y = ss.analogRead(3);
  
  if ( (abs(x - last_x) > 3)  ||  (abs(y - last_y) > 3)) {
    Serial.print(x); Serial.print(", "); Serial.println(y);
    last_x = x;
    last_y = y;
  }
  
  if(!digitalRead(IRQ_PIN)){
    uint32_t buttons = ss.digitalReadBulk(button_mask);
    //Serial.println(buttons, BIN);
    if (! (buttons & (1 << BUTTON_RIGHT))) {
      Serial.println("Button A pressed");
    }
    if (! (buttons & (1 << BUTTON_DOWN))) {
      Serial.println("Button B pressed");
    }
    if (! (buttons & (1 << BUTTON_LEFT))) {
      Serial.println("Button Y pressed");
    }
    if (! (buttons & (1 << BUTTON_UP))) {
      Serial.println("Button x pressed");
    }
    if (! (buttons & (1 << BUTTON_SEL))) {
      Serial.println("Button SEL pressed");
    }
  }
  delay(10);
}

are these the actual button presses?
Code:
  if(!digitalRead(IRQ_PIN)){
    uint32_t buttons = ss.digitalReadBulk(button_mask);
    //Serial.println(buttons, BIN);
    if (! (buttons & (1 << BUTTON_RIGHT))) {
      Serial.println("Button A pressed");
    }
    if (! (buttons & (1 << BUTTON_DOWN))) {
      Serial.println("Button B pressed");
    }
    if (! (buttons & (1 << BUTTON_LEFT))) {
      Serial.println("Button Y pressed");
    }
    if (! (buttons & (1 << BUTTON_UP))) {
      Serial.println("Button x pressed");
    }
    if (! (buttons & (1 << BUTTON_SEL))) {
      Serial.println("Button SEL pressed");
    }
  }

if those are the button presses which part takes care of the joy sticks?
 
well you were right about reaction times. I visually could not see a time delay between pressing the button and the buttons name appearing in the serial monitor box.

I just cant seem to get both to work at the same time. The joys have LED's which lets me know which is working yay! but I cant get both lit. I think my second address I put into begin works but I don't know which parts of the sketch need to be doubled to work both sides at once

Code:
bool Adafruit_seesaw::begin(uint8_t addr1, uint8_t addr2)
{
	_i2caddr = addr1;
	_i2caddr2 = addr2;

	_i2c_init();

	SWReset();
	delay(500);

	uint8_t c = this->read8(SEESAW_STATUS_BASE, SEESAW_STATUS_HW_ID);

	if(c != SEESAW_HW_ID_CODE) return false;
	
	return true;
}
 
did you try the original library with both devices attached to pins 18/19 bus of your teensy 3.x?

post #60 shows how to address both on the same bus
 
ok I doubled the parts I think are need but I still cant get both joys running at the same time...

Code:
#include "Adafruit_seesaw.h"
Adafruit_seesaw ss;
#define BUTTON_RIGHT 6  /////////////what are these integers
#define BUTTON_DOWN  7  /////////////"                      "
#define BUTTON_LEFT  9  /////////////"                      "
#define BUTTON_UP    10 /////////////"                      "
#define BUTTON_SEL   14 /////////////"                      "
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
uint32_t button_mask = (1 << BUTTON_RIGHT) | (1 << BUTTON_DOWN) | 
                (1 << BUTTON_LEFT) | (1 << BUTTON_UP) | (1 << BUTTON_SEL);
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
uint32_t button_mask2 = (1 << BUTTON_RIGHT) | (1 << BUTTON_DOWN) | 
                (1 << BUTTON_LEFT) | (1 << BUTTON_UP) | (1 << BUTTON_SEL);
                
#if defined(ESP8266)
  #define IRQ_PIN   2
#elif defined(ESP32)
  #define IRQ_PIN1   13
  #define IRQ_PIN2   27
#elif defined(NRF52)
  #define IRQ_PIN   27
#elif defined(TEENSYDUINO)
  #define IRQ_PIN   8
#elif defined(ARDUINO_ARCH_WICED)
  #define IRQ_PIN   PC5
#else
  #define IRQ_PIN   5
#endif
void setup() {
  Serial.begin(115200);
  while(!Serial);
  
  if(!ss.begin(0x49,0x4a)){
    Serial.println("ERROR!");
    while(1);
  }
  else{
    Serial.println("seesaw started");
    Serial.print("version: ");
    Serial.println(ss.getVersion(), HEX);
  }
  ss.pinModeBulk(button_mask, INPUT_PULLUP);
  ss.setGPIOInterrupts(button_mask, 1);
  pinMode(IRQ_PIN1, INPUT);
  
   ss.pinModeBulk(button_mask2, INPUT_PULLUP);
  ss.setGPIOInterrupts(button_mask2, 1);
  pinMode(IRQ_PIN2, INPUT);
}

int last_x = 0, last_y = 0;
int last_x2 = 0, last_y2 = 0;

void loop() {
  int x = ss.analogRead(2);
  int y = ss.analogRead(3);
  
  if ( (abs(x - last_x) > 3)  ||  (abs(y - last_y) > 3)) {
    Serial.print(x); Serial.print(", "); Serial.println(y);
    last_x = x;
    last_y = y;
  }
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
int x2 = ss.analogRead(2);
  int y2 = ss.analogRead(3);
  
  if ( (abs(x - last_x2) > 3)  ||  (abs(y - last_y2) > 3)) {
    Serial.print(x); Serial.print(", "); Serial.println(y);
    last_x2 = x2;
    last_y2 = y2;
  }
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
  if(!digitalRead(IRQ_PIN1)){
    uint32_t buttons = ss.digitalReadBulk(button_mask);
    //Serial.println(buttons, BIN);
    if (! (buttons & (1 << BUTTON_RIGHT))) {
      Serial.println("Button A pressed");
    }
    if (! (buttons & (1 << BUTTON_DOWN))) {
      Serial.println("Button B pressed");
    }
    if (! (buttons & (1 << BUTTON_LEFT))) {
      Serial.println("Button Y pressed");
    }
    if (! (buttons & (1 << BUTTON_UP))) {
      Serial.println("Button x pressed");
    }
    if (! (buttons & (1 << BUTTON_SEL))) {
      Serial.println("Button SEL pressed");
    }
  }
  if(!digitalRead(IRQ_PIN2)){
    uint32_t buttons = ss.digitalReadBulk(button_mask2);
    //Serial.println(buttons, BIN);
    if (! (buttons & (1 << BUTTON_RIGHT))) {
      Serial.println("Button A pressed");
    }
    if (! (buttons & (1 << BUTTON_DOWN))) {
      Serial.println("Button B pressed");
    }
    if (! (buttons & (1 << BUTTON_LEFT))) {
      Serial.println("Button Y pressed");
    }
    if (! (buttons & (1 << BUTTON_UP))) {
      Serial.println("Button x pressed");
    }
    if (! (buttons & (1 << BUTTON_SEL))) {
      Serial.println("Button SEL pressed");
    }
  }
  delay(10);
}
 
i only see one construction “Adafruit_seesaw ss;” using ur modified version

on post #60 shows how to run both, then you can do:

seesaw1.analogRead(2); for 0x49
and
seesaw2.analogRead(2); for 0x4A
 
Sorry I didn't know there was another post before posting my last post. Awesome thank you!!!! They both work now.

when looking at the serial monitor the joys put out a constant string of integers so when you press the tactile buttons you only see a flash of the button pressed. I take it this normal using the interrupts?
 
OK how do I turn this analog stuff.....

Code:
  if ( (abs(x - last_x) > 3)  ||  (abs(y - last_y) > 3)) {
    Serial.print(x); Serial.print(", "); Serial.println(y);
    last_x = x;
    last_y = y;
  }

into something like this?

Code:
  if(tft.Brepeat(BTN_UP,1)){
      tft.writeRectNBPP(player.player_x, player.player_y,  16, 16, 4, paulrearwa, palette);
      tft.writeRectNBPP(player.player_x, player.player_y, 16, 16, 4, paulrearwb, palette);
      
    player.player_direction = 1;
     player.player_y -= 4;
     if(checkcolision())
    {
        player.player_y += 4;} 
    }
     if(player.player_y <= 16){
        player.player_y = 16;}  
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   if(tft.Brepeat(BTN_DOWN,1)){
     tft.writeRectNBPP(player.player_x, player.player_y,16,16,4,paulfrontwa,palette);
    tft.writeRectNBPP(player.player_x, player.player_y,16,16,4,paulfrontwb,palette);
    player.player_direction = 2;
    player.player_y += 4;
    if(checkcolision())
    {
       player.player_y -=4;}
    }
    if(player.player_y >= 224){
       player.player_y = 224;}
 
ok I don't know if I did this right but it compiles and works but its real spotty.
Code:
 if ( (abs(x - last_x) > 3)){
  Serial.println("right");
   last_x = x;
 }

  if ( (abs(x + last_x) < 3)){
  Serial.println("left");
   last_x = x;
 }
 
 if ( (abs(y + last_y) < 3)){
  Serial.println("up");
   last_y = y;
 }
 
  if ( (abs(y - last_y) > 3)){
  Serial.println("down");
   last_y = y;
 }

how do I use bounce with this library?
 
Last edited:
bounce works on physical pins of the host itself, not sure if it works on an external board

looking through the adafruit examples, its safe to assume it doesnt use interrupts, your basically polling the chip from your own code. However, looking at their website, the user can optionally wire a specific gpio which is an interrupt for the user to control

https://learn.adafruit.com/adafruit-seesaw-atsamd09-breakout/interrupts

then, you could probably use that instead of polling to verify button state changes.

polling or interrupts wouldnt affect the game response anyways
 
Last edited:
I could have swore someone told me to use bounce with. Oh well. I wonder if there's a way to make the buttons repeat though.

Uhhmmm is there some kind of calibration I could do. The analogs are constantly outputting movement in the serial monitor. Seems to be stuck on down which means when the player screen comes on the player will constantly be moving down.
 
honestly, i could understand that “seesaw” device was a purpose made for the ESP due to lack of interfaces/pins, but you dont even need that if you wire directly to your teensy instead of just using an addon board, teensy is more capable than that control board and the game code could easily use local hardware instead of attached hardware
 
Status
Not open for further replies.
Back
Top