Hi all,
I have a project to use an Arduino as a translation device between an XBOX controller and a remote control vehicle transmitter by supplying the transmitter with a ppm signal. The fundamental operation of the system is working well, and I can currently use an XBOX controller to operate an RC sailing boat.
Just now I am trying to add an LCD to the project and having troubles. To get started I thought it best to simply add the basic code to my existing sketch to get the LCD to display Hello World, and then move on to getting it to display the desired information. This is where I am stuck .... my best efforts and my LCD remains stubbornly blank (though lit).
To test I have wired the LCD correctly I uploaded a basic LCD sketch that only controls the LCD and nothing else, and this worked fine. There is something in my project code that is conflicting with the LCD, but I can't spot it.
This is my code. Can anyone spot the problem?
#include <XBOXONE.h>
#include <LiquidCrystal.h>
// Satisfy the IDE, which needs to see the include statment in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#endif
#include <SPI.h>
USB Usb;
XBOXONE Xbox(&Usb);
bool printAngle;
uint8_t state = 0;
LiquidCrystal lcd(8, 9, 10, 11, 12, 13);
////////////
/*
* PPM generator originally written by David Hasko
* on https://code.google.com/p/generate-ppm-signal/
*/
//////////////////////CONFIGURATION///////////////////////////////
#define CHANNEL_NUMBER 8 //set the number of chanels
#define CHANNEL_DEFAULT_VALUE 1500 //set the default servo value
#define FRAME_LENGTH 22500 //set the PPM frame length in microseconds (1ms = 1000µs)
#define PULSE_LENGTH 300 //set the pulse length
#define onState 1 //set polarity of the pulses: 1 is positive, 0 is negative
#define sigPin 2 //set PPM signal output pin on the arduino
//////////////////////////////////////////////////////////////////
#define SWITCH_PIN 16
#define CHANNEL_TO_MODIFY 11
#define SWITCH_STEP 100
byte previousSwitchValue;
/*this array holds the servo values for the ppm signal
change these values in your code (usually servo values move between 1000 and 2000)*/
int ppm[CHANNEL_NUMBER];
float ppm2Adjust = 1500;
int currentChannelStep;
int rudderTrim = 0;
/////////////
void setup() {
Serial.begin(115200);
#if !defined(__MIPSEL__)
while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
if (Usb.Init() == -1) {
Serial.print(F("\r\nOSC did not start"));
while (1); //halt
}
Serial.print(F("\r\nXbox USB Library Started"));
///////////////////////////////
previousSwitchValue = HIGH;
//initiallize default ppm values
for(int i=0; i<CHANNEL_NUMBER; i++){
if (i == 2 || i == CHANNEL_TO_MODIFY) {
ppm[i] = 1000;
} else {
ppm[i]= CHANNEL_DEFAULT_VALUE;
}
}
ppm[2] = 1500; // Set ppm[2] (i.e. RC Channel 3) to midway point to allow for incremental up/down adjustment in the loop
ppm2Adjust = 1500;
pinMode(sigPin, OUTPUT);
pinMode(SWITCH_PIN, INPUT_PULLUP);
digitalWrite(sigPin, !onState); //set the PPM signal pin to the default state (off)
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// Print a message to the LCD.
lcd.print("hello, world!");
cli();
TCCR1A = 0; // set entire TCCR1 register to 0
TCCR1B = 0;
OCR1A = 100; // compare match register, change this
TCCR1B |= (1 << WGM12); // turn on CTC mode
TCCR1B |= (1 << CS11); // 8 prescaler: 0,5 microseconds at 16mhz
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
sei();
currentChannelStep = SWITCH_STEP;
}
/* Channel Info Currently Setup AS AETR you can change this by changing the PPM Value in the main loop
*
Ch1 A (Rudder) == ppm[0]
Ch2 E (NOT SET) == ppm[1]
Ch3 T (Sail Trim) == ppm[2]
Ch4 R (NOT SET) == ppm[3]
Ch5 AUX1 (NOT SET) == ppm[4]
Ch6 AUX2 (NOT SET) == ppm[5]
Ch7 AUX3 (NOT SET) == ppm[6]
Ch8 AUX4 (NOT SET) == ppm[7]
*/
void loop() {
Usb.Task();
if (Xbox.XboxOneConnected) {
// Rudder
// Attached to Xbox Left Joystick X Axis, inverted so that left joystick = steer left (swap the 2000 and 1000 to change this)
// Trim adjusted by pressing L1 or L3 as these are available buttons with Quadstick plugged into XAC, X and Y not available.
if (Xbox.getButtonClick(L1)){ // Incrementally trim rudder to turn left
rudderTrim = rudderTrim + 5;
//Serial.print(F("\r\nA"));
//Serial.print(rudderTrim);
}
if (Xbox.getButtonClick(L3)){ // Incrementally trim rudder to turn right
rudderTrim = rudderTrim - 5;
//Serial.print(F("\r\nB"));
//Serial.print(rudderTrim);
}
if (Xbox.getButtonClick(BACK) || Xbox.getButtonClick(START)){ // Reset rudder trim to 0
rudderTrim = 0;
//Serial.print(F("\r\nB"));
//Serial.print(rudderTrim);
}
ppm[0] = map(Xbox.getAnalogHat(LeftHatX), -32768 , 32768, 2000, 1000) + rudderTrim; // Get value of X axis on joystick, adjust for trim then publish to ppm value
// Sail trim
// Attached to Xbox Left Joystick Y Axis
// 'IF' statements do three things: 1. provide a deadband, 2. provide incremental adjustment of a ppm value,
// allowing a spring-centering joystick to be used for something not normally suited to that style of
// joystick, ie. sail trim, and 3. provide a modicum of proportional joystick operation for this adjustment
long servoLeftHatY = Xbox.getAnalogHat(LeftHatY); //Get value of Y axis on joystick and place in a variable
//Serial.println(servoLeftHatY);
if (servoLeftHatY < -5000 && ppm[2] < 2000) {
ppm2Adjust = ppm2Adjust + 0.01;
ppm[2] = round(ppm2Adjust);
}
if (servoLeftHatY < -25000 && ppm[2] < 2000) {
ppm2Adjust = ppm2Adjust + 0.09;
ppm[2] = round(ppm2Adjust);
}
if (servoLeftHatY > 5000 && ppm[2] > 1000) {
ppm2Adjust = ppm2Adjust - 0.01;
ppm[2] = round(ppm2Adjust);
}
if (servoLeftHatY > 25000 && ppm[2] > 1000) {
ppm2Adjust = ppm2Adjust - 0.09;
ppm[2] = round(ppm2Adjust);
}
// Set sail trim all the way out/loose by single button press
if (Xbox.getButtonClick(A)){
ppm[2] = 2000;
}
// Set sail trim all the way in/tight by single button press
if (Xbox.getButtonClick(B)){
ppm[2] = 1000;
}
// All available XBOX Buttons
// if (Xbox.getButtonClick(UP))
// Serial.println(F("Up"));
// if (Xbox.getButtonClick(DOWN))
// Serial.println(F("Down"));
// if (Xbox.getButtonClick(LEFT))
// Serial.println(F("Left"));
// if (Xbox.getButtonClick(RIGHT))
// Serial.println(F("Right"));
//
// if (Xbox.getButtonClick(START))
// Serial.println(F("Start"));
// if (Xbox.getButtonClick(BACK))
// Serial.println(F("Back"));
// if (Xbox.getButtonClick(XBOX))
// Serial.println(F("Xbox"));
// if (Xbox.getButtonClick(SYNC))
// Serial.println(F("Sync"));
//
// if (Xbox.getButtonClick(L1))
// Serial.println(F("L1"));
// if (Xbox.getButtonClick(R1))
// Serial.println(F("R1"));
// if (Xbox.getButtonClick(L2))
// Serial.println(F("L2"));
// if (Xbox.getButtonClick(R2))
// Serial.println(F("R2"));
// if (Xbox.getButtonClick(L3))
// Serial.println(F("L3"));
// if (Xbox.getButtonClick(R3))
// Serial.println(F("R3"));
//
//
// if (Xbox.getButtonClick(A))
// Serial.println(F("A"));
// if (Xbox.getButtonClick(B))
// Serial.println(F("B"));
// if (Xbox.getButtonClick(X))
// Serial.println(F("X"));
// if (Xbox.getButtonClick(Y))
// Serial.println(F("Y"));
// if (Xbox.getButtonPress(L2) > 0 || Xbox.getButtonPress(R2) > 0) {
// if (Xbox.getButtonPress(L2) > 0) {
// Serial.print(F("L2: "));
// Serial.print(Xbox.getButtonPress(L2));
// Serial.print("\t");
// }
// if (Xbox.getButtonPress(R2) > 0) {
// Serial.print(F("R2: "));
// Serial.print(Xbox.getButtonPress(R2));
// Serial.print("\t");
// }
// Serial.println();
// }
}
if (Xbox.XboxOneConnected == false){
ppm[4] = 1000;
}
}
ISR(TIMER1_COMPA_vect){ //leave this alone
static boolean state = true;
TCNT1 = 0;
if (state) { //start pulse
digitalWrite(sigPin, onState);
OCR1A = PULSE_LENGTH * 2;
state = false;
} else{ //end pulse and calculate when to start the next pulse
static byte cur_chan_numb;
static unsigned int calc_rest;
digitalWrite(sigPin, !onState);
state = true;
if(cur_chan_numb >= CHANNEL_NUMBER){
cur_chan_numb = 0;
calc_rest = calc_rest + PULSE_LENGTH;//
OCR1A = (FRAME_LENGTH - calc_rest) * 2;
calc_rest = 0;
}
else{
OCR1A = (ppm[cur_chan_numb] - PULSE_LENGTH) * 2;
calc_rest = calc_rest + ppm[cur_chan_numb];
cur_chan_numb++;
}
}
}