Pages: [1]   Go Down
Author Topic: First post, first project  (Read 1014 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm new here, new to Arduino, and relatively new to programming anything other than shell script and a little HTML/CSS and javascript.  Looking to see if there is any interest in this project, as well as for someone to review my code and see if I made any major mistakes.  It compiles and loads, but I won't have the hardware built to test it out for a couple of weeks (damn cash flow.....).  Anways, after digging around, I couldn't find anything else quite like what I wanted, but I found a project that gave me a starting point, at least with the hardware design, and a couple of libraries tht made my life easier.  I am attempting to build a dual wireless controller adapter for the old 8-bit consoles.  In my case, specifically the SNES, but it could easily be adapted for any of the older Nintendo and Sega consoles, and theoretically could probably handle four controllers instead of two with some tweaking and extra hardware.  My design uses an NRF24l01 wireless board on each end, tied to a 328p.  The transmitter will have the game port board from a dead SNES (I have a pile of spare parts....), will read in the controller button states using a modified version of the snesPad library, and transmit the 4 byte packet to the reciever (using Maniacbugs NRF24 library).  The reciever will take the button states and push them out to the console. The transmitter end is fairly small using the stock libraries.  I made a few tweaks to the snesPad library to have it poll both controllers at the same time rather than just one, and then concatonate the two 16bit ints into an unsigned long and return that to be transmitted.  The reciever end is where things get a little dicey for me.  I couldn't use the snesPad library, as it's designed to read the shift registers from the controllers, not to act as a shift register to be read by the console.  I don't know if the solution I came up with will actually work, but it does compile.  So.... let me know what you all think, whether I'm on the right track, and if you think it will work.

Here's the transmitter code

Code:
/*
Copyright (C) 2012 John Byers <jbyers2@wgu.edu>

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 3 as published by the Free Software Foundation
*/

/**
 * Dual Wireless Retro Controller Adapter Transmitter
 *
 * This is the transmitter side code for the adapter set.
 */

#include <SNESpadDual.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>

//
// Hardware Configuration
//

RF24 radio(9,10);
SNESpad nintendo = SNESpad(4,3,5,6);

//
// Variable Inits
//

unsigned long state = 0;
const uint64_t pipes = 0xF0F0F0F0E1LL;

void setup()
{
  radio.begin();
  radio.setRetries(15,15);
  Serial.begin(57600);
}

void loop() {
 
  // Get controller button states and write out to serial monitor for debugging 
  state = nintendo.buttons();
  Serial.println(~state, BIN);
 
  // Send button states to reciever and output transmission status for debugging
  bool ok = radio.write( &state, sizeof(unsigned long) );
  if (ok)
    Serial.println("ok...");
  else
    Serial.println("failed!!");
}

And here is the reciever code

Code:
/*
Copyright (C) 2012 John Byers <jbyers2@wgu.edu>

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 3 as published by the Free Software Foundation
*/

/**
 * Dual Wireless Retro Controller Adapter Transmitter
 *
 * This is the reciever side code for the adapter set.
 */

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>

//
// Hardware Configuration
//

RF24 radio(9,10);

//
// Variable Inits
//

unsigned long state = 0;
const uint64_t pipes = 0xF0F0F0F0E1LL;
int strobe = 4;
int clock = 3;
int data1 = 5;
int data2 = 6;

void setup()
{
  radio.begin();
  radio.setRetries(15,15);
  Serial.begin(57600);
  pinMode(strobe, INPUT);
  pinMode(clock, INPUT);
  pinMode(data1, OUTPUT);
  pinMode(data2, OUTPUT);
 
  //
  //Open radio pipe for writing
  //
 
  radio.openReadingPipe(1,pipes);

  //
  //Start Listening
  //
 
  radio.startListening();

  //
  //Dump the configuration of the RF unit for debugging
  //

  radio.printDetails();
 
}

void loop()
{
  // Wait for data
  bool get_data = false;
  unsigned long state;
  bool timeout = false;
  if ( radio.available() ) {
    unsigned long started_waiting_at = millis();
    radio.read( &state, sizeof(unsigned long) );
    Serial.println(state, BIN);
 
    // Data captured, now wait for console to trigger latch
    while ( ! digitalRead(strobe) && ! timeout ) {
      if ( millis() - started_waiting_at > 200 ) {
        timeout = true;
      }
    }
    get_data = true;
  }
 
  if ( timeout ) {
    Serial.write("Console strobe timeout!!!");
  }
  else if ( ! get_data) {
    Serial.write("No data from transmitter!!!!");
  }
  else {
    // Data captured and latch triggered, output button states
    int data_bit1;
    int data_bit2;
    byte i;
    digitalWrite(data1,bitRead(state,0));
    digitalWrite(data1,bitRead(state,16));
    while ( i = 1, i < 16, i++ ) {
      while ( ! digitalRead(clock) ) {
        delayMicroseconds(1);
      }
      digitalWrite(data1,bitRead(state,i));
      digitalWrite(data2,bitRead(state,(i+16)));
      delayMicroseconds(12);
    }
    Serial.println("sucess!!!");
  }
}

I was having a hell of a time trying to figure out how to read out the unsigned long bits to be written out to the console, untill I found the bitRead function.  The description in the Arduino docs is pretty minimal though, so I am not sure if I'm applying it correctly.  The first 16 bits of the unsigned long is the button states from controller one, the second 16 bits is the button states from the second controller.  The first bit read in is the first bit in the int variable, second bit read in is the second bit, etc...

For those unfamiliar with the SNES controller serial protocol, the controllers contrain two 8bit parallel to serial shift registers.  The console pulses a latch signal (24us cycle) high, which tells the registers to lock in the current button states (high=inactive).  The first bit is read on the falling edge of the latch pulse, and then each successive bit is set by the shift registers on the rising edge of a clock pulse (24us cycle), and read by the console on the falling edge.  The whole cycle repeats every 16ms or so.

The delay between read cycles is another problem I'm not sure about.  Unfortunately, I don't think I can determine a suitable delay untill I get actual hardware built and measure the read cycle time.  I don't want to overload the reciever by generating button states faster than the console requests them.

The only similar project I've found is designed to have a transmitter build into the controller and a seperate reciever for each controller.  While this is great for portability, it's not so great for hardware cost.  Sadly, the guy who designed it makes the hex files freely available, but not the source code.  Plus he writes in machine language, so I wouldn't have a clue what I was looking at anyways....

If I haven't bored you to tears by now, thank you for looking, and thank you in advance for any help!!
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

After much thought and reading, I've made some significant changes to my code to speed things up, and keep timings correct.  I switched to external interrupts to trigger the writing out of the bits on the reciever end, and setup a ping/pong mechanism to tell the transmitter when the reciever is ready for another data packet.  Here's the new code.

Transmitter code:

Code:
/*
Copyright (C) 2012 John Byers <jbyers2@wgu.edu>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 3 as published by the Free Software Foundation
*/

/**
 * Dual Wireless Retro Controller Adapter Transmitter
 *
 * This is the transmitter side code for the adapter set.
 */

#include <SNESpadDual.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>

//
// Hardware Configuration
//

RF24 radio(9,10);
SNESpad nintendo = SNESpad(4,3,5,6);

//
// Variable Inits
//

unsigned long state = 0;
const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };

void setup()
{
  radio.begin();
  radio.setRetries(0,15);
  radio.enableDynamicPayloads();
  Serial.begin(57600);

radio.openWritingPipe(pipes[0]);
radio.openReadingPipe(1,pipes[1]);
radio.startListening();
 
  //
  //Dump the configuration of the RF unit for debugging
  //
 
  radio.printDetails();
}

void loop() {

  int ready = 0;
  if (radio.available() ) {
    radio.read (&ready, sizeof(char));
  }
  if (ready=1) {
    radio.stopListening();
    // Get controller button states and write out to serial monitor for debugging
    state = nintendo.buttons();
    Serial.println(~state, BIN);
 
    // Send button states to reciever and output transmission status for debugging
    bool ok = radio.write( &state, sizeof(unsigned long) );
    if (ok) {
      Serial.println("transmission successful");
    }
    else {
      Serial.println("transmission failed!!");
    }
    radio.startListening();
  }
}

Reciever code:

Code:
/*
Copyright (C) 2012 John Byers <jbyers2@wgu.edu>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 3 as published by the Free Software Foundation
*/

/**
 * Dual Wireless Retro Controller Adapter Transmitter
 *
 * This is the reciever side code for the adapter set.
 */

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>

//
// Hardware Configuration
//

RF24 radio(9,10);

//
// Variable Inits
//

volatile unsigned long state2 = 0xFFFFFFFF;
volatile byte i = 0;
const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };
int strobe = 2;
int clock = 3;
volatile int data1 = 5;
volatile int data2 = 6;
bool firstLoop = true;
volatile int status2 = 1;

void setup()
{
  radio.begin();
  radio.setRetries(0,15);
  radio.enableDynamicPayloads();
  Serial.begin(57600);
  pinMode(strobe, INPUT);
  pinMode(clock, INPUT);
  pinMode(data1, OUTPUT); digitalWrite(data1, LOW);
  pinMode(data2, OUTPUT); digitalWrite(data2, LOW);

  radio.openWritingPipe(pipes[1]);
  radio.openReadingPipe(1,pipes[0]);
 
  //
  //Dump the configuration of the RF unit for debugging
  //

  radio.printDetails();

  //
  //Setup Interupts
  //
 
  attachInterrupt(strobe,latch,RISING);
  attachInterrupt(clock,data,RISING);

}

void loop()
{
  if (firstLoop) {
    int status1 = 1;
    bool ok = radio.write( &status1, sizeof(int));
    firstLoop = false;
    radio.startListening();
    if (!ok) {
      Serial.println("sync packet transmission failed");
    }
    else {
      Serial.println("sync packet transmission successful");
    }
  }
  if ( radio.available() )
  {
    unsigned long state = 0;
    radio.read( &state, sizeof(unsigned long) );
    Serial.println(state, BIN);
    state2 = state;
   
  }
  else
  {
    Serial.println("No data recieved yet");
  }
}

//Latch interrupt routine

void latch()
{
  i = 0;
  digitalWrite(data1,HIGH);
  digitalWrite(data2,HIGH);
  digitalWrite(data1,bitRead(state2,i));
  digitalWrite(data2,bitRead(state2,(i+16)));
  Serial.println("Bit0 out");
}

//Data interrupt routine

void data()
{
  i++;
  digitalWrite(data1,bitRead(state2,i));
  digitalWrite(data2,bitRead(state2,(i+16)));
  Serial.print("Bit");
  Serial.print(i);
  Serial.println(" out");
  if(i=15)
  {
    digitalWrite(data1,LOW);
    digitalWrite(data2,LOW);
    radio.stopListening();
    int status1 = status2;
    bool ok = radio.write( &status1, sizeof(int));
    if (!ok) {
      Serial.println("sync packet transmission failed");
    }
    else {
      Serial.println("sync packet transmission successful");
    }
    radio.startListening();
  }
}
Logged

Pages: [1]   Go Up
Jump to: