forked from lexfp/G25toPPM
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathG25toPPM.ino
More file actions
200 lines (162 loc) · 5.29 KB
/
Copy pathG25toPPM.ino
File metadata and controls
200 lines (162 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/* Simplified Thrustmaster T.16000M FCS Joystick Report Parser */
#include <usbhid.h>
#include <hiduniversal.h>
#include <usbhub.h>
#include "BtnPPMMap.h"
// Satisfy the IDE, which needs to see the include statment in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#endif
#include <SPI.h>
/**
This code will map the Logitech G25 steering wheel and pedals to PPM output. It will basically allow you to drive a RC car with the wheel.
@Author github.com/lexfp
*/
//PPM config values
#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 PPM_PIN 3 //set PPM signal output pin on the arduino
#define INPUT_SWITCH_1 6 //pin for switch 0
#define INPUT_SWITCH_2 7 //pin for switch 1
BtnPPMMap btnPPMMap;
// Logitech G25 steering wheel - only the wheel, pedals & shifter are mapped
struct GamePadEventData
{
uint16_t wheel;
uint8_t shift;
uint32_t gas;
} __attribute__((packed));
class JoystickEvents
{
private:
BtnPPMMap *btnPPMMap;
public:
JoystickEvents(BtnPPMMap *btnPPMMap);
virtual void OnGamePadChanged(const GamePadEventData *evt);
virtual void disconnectJoystick();
};
#define RPT_GAMEPAD_LEN sizeof(GamePadEventData)
class JoystickReportParser : public HIDReportParser
{
JoystickEvents *joyEvents;
uint8_t oldPad[RPT_GAMEPAD_LEN];
public:
JoystickReportParser(JoystickEvents *evt);
virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};
JoystickReportParser::JoystickReportParser(JoystickEvents *evt) :
joyEvents(evt)
{}
void JoystickReportParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf)
{
// Checking if there are changes in report since the method was last called
bool match = (sizeof(oldPad) == len) && (memcmp(oldPad, buf, len) == 0);
// Calling Game Pad event handler
if (!match && joyEvents) {
joyEvents->OnGamePadChanged((const GamePadEventData*)buf);
memcpy(oldPad, buf, len);
}
}
JoystickEvents::JoystickEvents(BtnPPMMap * bpm) {
btnPPMMap = bpm;
}
void JoystickEvents::disconnectJoystick()
{
btnPPMMap->disArm();
}
void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt)
{
Serial.print("IN - T:");
Serial.print(evt->gas, DEC);
Serial.print(" W:");
Serial.print(evt->wheel, DEC);
Serial.print(" S:");
Serial.print(evt->shift, DEC);
Serial.println();
btnPPMMap->mapShift(evt->shift);
btnPPMMap->mapGas(evt->gas);
btnPPMMap->mapSteer(evt->wheel);
btnPPMMap->debug();
}
USB Usb;
USBHub Hub(&Usb);
HIDUniversal Hid(&Usb);
JoystickEvents JoyEvents(&btnPPMMap);
JoystickReportParser Joy(&JoyEvents);
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
Serial.println("Start");
btnPPMMap.debug();
if (Usb.Init() == -1)
Serial.println("OSC did not start.");
delay( 200 );
if (!Hid.SetReportParser(0, &Joy))
ErrorMessage<uint8_t>(PSTR("SetReportParser"), 1 );
pinMode(PPM_PIN, OUTPUT);
digitalWrite(PPM_PIN, !onState); //set the PPM signal pin to the default state (off)
//set up switches for car selection
pinMode( INPUT_SWITCH_1, INPUT_PULLUP );
pinMode( INPUT_SWITCH_2, INPUT_PULLUP );
//select the car
if (digitalRead( INPUT_SWITCH_1 ) == 0) {
btnPPMMap.changeCar(CAR_MICRO_T);
Serial.println("-----CAR_MICRO_T--------");
} else if (digitalRead( INPUT_SWITCH_2 ) == 0) {
btnPPMMap.changeCar(CAR_MICRO_TRUGGY);
Serial.println("-----CAR_MICRO_TRUGGY--------");
} else {
//default if switches are not wired
btnPPMMap.changeCar(CAR_MINI_Q5);
Serial.println("-----CAR_MINI_Q5--------");
}
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();
}
void loop()
{
Usb.Task();
if (Usb.getUsbTaskState() != USB_STATE_RUNNING)
{
//disconnected joystick
JoyEvents.disconnectJoystick();
//Serial.println("Joystick disconnected");
}
}
ISR(TIMER1_COMPA_vect) { //leave this alone
static boolean state = true;
TCNT1 = 0;
if (state) { //start pulse
digitalWrite(PPM_PIN, onState);
OCR1A = PULSE_LENGTH * 2;
state = false;
} else { //end pulse and calculate when to start the next pulse
static byte cur_chan_numb;
int channelValue;
static unsigned int calc_rest;
digitalWrite(PPM_PIN, !onState);
state = true;
if (cur_chan_numb >= btnPPMMap.getNumChannels()) {
cur_chan_numb = 0;
calc_rest = calc_rest + PULSE_LENGTH;//
OCR1A = (FRAME_LENGTH - calc_rest) * 2;
calc_rest = 0;
}
else {
channelValue = btnPPMMap.getChannelValue(cur_chan_numb);
OCR1A = (channelValue - PULSE_LENGTH) * 2;
calc_rest = calc_rest + channelValue;
cur_chan_numb++;
}
}
}