1/ Arduino DUE
1.1/ Keyboard : (attached to "FS Hat" PC)
Arduino DUE has to behave as a keyboard, so I will use the <keyboard.h> lib. But all the keys are not included in this lib, so an extension is needed to add extra keys:
keyboard_ext.h ______________________________________________________________
/*
Keyboard_ext.h
keyboard extension adapted from
*/
//================================================================================
// extended Keyboard
#define KEY_PAUSE 0xD0
#define KEY_PRINT_SCREEN 0xCE
#define KEY_SCROLL_LOCK 0xCF
#define KEYPAD_NUMLOCK 0xDB
#define KEYPAD_0 0xEA
#define KEYPAD_1 0xE1
#define KEYPAD_2 0xE2
#define KEYPAD_3 0xE3
#define KEYPAD_4 0xE4
#define KEYPAD_5 0xE5
#define KEYPAD_6 0xE6
#define KEYPAD_7 0xE7
#define KEYPAD_8 0xE8
#define KEYPAD_9 0xE9
#define KEYPAD_SLASH 0xDC
#define KEYPAD_ASTERISK 0xDD
#define KEYPAD_MINUS 0xDE
#define KEYPAD_PLUS 0xDF
#define KEYPAD_ENTER 0xE0
#define KEYPAD_PERIOD 0xEB
__________________________________________________________________________
1.2/ Digital inputs: (44 inputs)
As explained in the previous part, I exploit interrupts for each of them, attachInterrupt(pin#, _function, RISING);
Some of the inputs are not yet used, but foreseen for "future extension"...
In the interrupt _function, I just set a flag (flag |= specialBit) to be read in the main loop. Once tested in the main loop (if flag & specialBit), a special action/function is processed.
I use 3 kind of flags; either a key has to be written to the keyboad (kbCharW), or a char/chain has to be sent to serial link (sndStrFlag), or a key has to be first pressed or released (kbCharPR).
To avoid using many loops to test the bits of the 3 flags (3 x 3 loops), I'd gathered them in common loops (as long as the number of bits to test are equal)
1.3/ Analog inputs: (Steering wheel, accel pedal, accel hand, brakes pedal)
A0 to A3 values are scaled with my own map function (see why below in analog outputs part). I had preferred tuning my inputs in the range 0.1V to 3.2V (see hardware) to avoid negative/overvoltage on the arduino.
curVal = myMap(analogRead(analogInputPin), 124, 3971);
In the 12 bits resolution range [0, 4095] for [0V, 3.3V], [124, 3971] corresponds to [0.1V, 3.2V].
However, even if the potentiometers powered with max +/- 15 V (see hardware), some analog inputs max value (accelerations) were around 2.4 V. That's why I had to adjust the "3971" values for them.
The scaled values are sent directly to the analog outputs.
1.4/ Analog outputs: (Steering wheel, accel forward, accel backward, brakes)
As already mentioned, I had preferred using PWM as I needed 4 analog outputs in the full range [0, 3.3V] voltage.
However, this feature needs some code changes in the arduino lib, to set 12 bits resolution and high PWM frequency (even if I filter @80Hz, to be far from Shannon critical frequencies).
So in C:\Users\{user}\AppData\Local\Arduino15\packages\arduino\hardware\sam\1.6.11\variants\arduino_due_x\variant.h
values to set (in bold):
/*
* PWM
*/
#define PWM_INTERFACE PWM
#define PWM_INTERFACE_ID ID_PWM
#define PWM_FREQUENCY 6000
#define PWM_MAX_DUTY_CYCLE 4095
#define PWM_MIN_DUTY_CYCLE 0
#define PWM_RESOLUTION 12
During the testings, I spent a lot of time to find an error that occured on my analog outputs. The system crashed each time I was at an extremum value of my potentiometers.
I had to use the serial monitor to find the error message duty <= pPwm->PWM_CH_NUM[ul_channel].PWM_CPRD" failed: file "../source/pwmc.c", line 272, function: PWMC_SetDutyCycle
I finally found on internet (thanks google), that it came from the arduino map function (it sometimes returns negative values, which is incompatible with PWM function)! See here.
So I wrote my own map function:
unsigned int myMap(float val, float minin, float maxin) {
return min((unsigned int) max(0., (val - minin) * (float)maxV12 / (maxin - minin)), maxV12);
}
1.5/ Serial link:
Set to 115200 bps, it receives and sends data.To gain in reactivity I use just a few bytes (some characters).
A first loop retreives all the incoming characters (while (Serial.available())) while in the other scanning loops the bits are tested to know which chain to send to the serial link.
All the data sent to the serial link will be forwarded to TCP/IP with "PS_Hat" PC. So, to optimise the TCP/IP bandwidth (even if it's in a 1Gb/s local net, yes limited to 100Mb/s due the the raspberry pis), I set a flag to limit the sendings.
okToSend = ((unsigned long)(t1 - t0) >= dt); // cast to avoid to manage roll-over
BTW, here how to handle millis roll over ...
inputsDUE.c _______________________________________________________________
/*
Program Arduino DUE
Manage tractor inputs for Farming Simulator
Damien Hallez, nov 2017
*/
#include <Keyboard.h>
#include "Keyboard_ext.h"
// ======================================================================================
// DECLARATIONS
// ======================================================================================
// ----- ex IA1
// const short pin_JSC_Left = 52 ; // ---- Joystick Cabin Left
// const short pin_JSC_Right = 68 ; // ---- Joystick Cabin Right
// const short pin_JSC_Frwd = 50 ; // ---- Joystick Cabin Forward
// const short pin_JSC_Bkwd = 69 ; // ---- Joystick Cabin Backward
// const short pin_FSp_PB = 64 ; // ---- Fix speed button
const short pin_Neutral = 47 ; // ---- Neutral for/back ward
const short pin_Horn = 42 ; // ---- Horn => '0_'
const short pin_Beacon = 16 ; // ---- Beacon => 'HOME' + beacon= to Rpi_Disp & Rpi_Out
// ----- ex IB1
// const short pin_JSC_H4 = 65 ; // ---- Joystick Cabin H4
// const short pin_JSC_H3 = 62 ; // ---- Joystick Cabin H3
// const short pin_JSC_Pl = 66 ; // ---- Joystick Cabin +
// const short pin_JSC_Ms = 63 ; // ---- Joystick Cabin -
// const short pin_JSC_PB = 64 ; // ---- Joystick Cabin Push Button
const short pin_SeqLev_PB = 48 ; // ---- Sequential lever Push Button => 'O'
// const short pin_SeqLev_SP = 53 ; // ---- Sequential lever speed +
// const short pin_SeqLev_SM = 46 ; // ---- Sequential lever speed -
// ----- ex IA2
const short pin_Li_FrDown = 14 ; // ---- Lights Front Low => 'F/3F' + LghtFrDown/LghtReDown to Rpi_Disp & Rpi_Out
const short pin_Li_FrUp = 17 ; // ---- Lights Front Up => 'KP5' + LghtFrUp to Rpi_Disp & Rpi_Out
const short pin_Li_ReDown = 15 ; // ---- Lights Rear Low => 'F/3F' + LghtFrDown/LghtReDown to Rpi_Disp & Rpi_Out
const short pin_Li_ReUp = 18 ; // ---- Lights Rear Up => 'KP6' + LghtReUp to Rpi_Disp & Rpi_Out
// const short pin_PB1 = 19 ; // ---- Push Button 1
// const short pin_PB2 = 20 ; // ---- Push Button 2
// const short pin_PB3 = 26 ; // ---- Push Button 3
// const short pin_PB5 = 28 ; // ---- Push Button 5
// ----- ex IB2
// const short pin_Frwd_Up = 53 ; // ---- sequential forward speed Up
// const short pin_Frwd_Down = 46 ; // ---- sequential forward speed Down
// const short pin_Bkwd_Up = 53 ; // ---- sequential backward speed Up
// const short pin_Bkwd_Down = 46 ; // ---- sequential backward speed Down
const short pin_Warning = 37 ; // ---- Warning button => 'KP2' + Warning=to Rpi_Disp & Rpi_Out
const short pin_BP4_N = 21 ; // ----- N on lights board => 'B'
const short pin_RightBlinker = 49 ; // ----- Right Blinker => 'KP3'
const short pin_LeftBlinker = 44 ; // ----- Left Blinker => 'KP1'
// ----- ex IA3
// const short pin_SV1 = 36 ; // ---- SV1
const short pin_A = 38 ; // ---- A
const short pin_UTurn = 40 ; // ---- U-turn => 'G'
const short pin_LG_Up = 29 ; // ---- Lift Gear Up => 'V'
const short pin_LG_Down = 33 ; // ---- Lift Gear Down => 'V'
const short pin_PosN_1 = 31 ; // ---- Position N => 'B'
// ----- ex IB3
const short pin_HydrauLeftLeverFrwd = 41 ; // => 'X'
const short pin_HydrauLeftLeverBkwd = 51 ; // => 'X'
const short pin_HydrauRightLeverFrwd = 43 ; // => 'I'
const short pin_HydrauRightLeverBkwd = 39 ; // => 'I'
const short pin_GreenButtonLeftJS = 27 ; // => 'C'
const short pin_RedButtonLeftJS = 30 ; // => 'BS'
const short pin_GreenButtonRightJS = 22 ; // => 'PgUp'
const short pin_RedButtonRightJS = 24 ; // => 'PgDwn'
// ----- ex on Rpi
const short pin_FrwdInfo = 25 ; //
const short pin_BkwdInfo = 32 ; //
const short pin_FrwdBkwdToggle = 67 ; //
// ----- spares
// const short pinSpare1 = 34
// const short pinSpare2 = 23
// const short pinSpare3 = 45
// const short pinSpare4 = 35
// ----- Toggle buttons:
volatile boolean hornOn = false;
volatile boolean beaconOn = false;
volatile boolean warningOn = false;
volatile boolean Li_fr_down = false;
volatile boolean Li_re_down = false;
volatile boolean Li_fr_up = false;
volatile boolean Li_re_up = false;
volatile boolean Lg_up = false;
volatile boolean leftBlinkerOn = false;
volatile boolean rightBlinkerOn = false;
volatile boolean zoomInOn = false;
volatile boolean zoomOutOn = false;
volatile boolean cameraOn = false;
volatile short Direction = 0;
// ----- ADC
const short in_steeringW = A0;
const short in_acc_pedal = A1;
const short in_acc_hand = A2;
const short in_brakes = A3;
int curStwVal = 0;
int prevStwVal = 0;
int curStwAngle = 0;
int prevStwAngle = 0;
int curAccel = 0;
int accelPedal = 0;
int accelHand = 0;
int prevAccel = 0;
int curAccel2 = 0;
int prevAccel2 = 0;
int curBrksVal = 0;
int prevBrksVal = 0;
boolean brakesOn = false;
const int maxV12 = 4095;
// ----- DAC - real PWM set @6kHz
const short out_steeringW = 7;
const short out_forward = 6;
const short out_bckward = 5;
const short out_brakes = 4;
// ----- Serial -> pins 18 & 19
char rcvString[10] ;
int nrcv = 0;
char sndString[15] ;
const short nStrAcc0 = 0;
const short nStrReRedLght = 1;
const short nStrBeacon = 2;
const short nStrLghtFrDown = 3;
const short nStrLghtReDown = 4;
const short nStrRightBlinker = 5;
const short nStrLeftBlinker = 6;
const short nStrWarning = 7;
const short nStrLghtFrUp = 8;
const short nStrLghtReUp = 9;
const short nStrToSend = 10;
char strToSend[nStrToSend][15];
unsigned int sndStrFlag = 0;
const short bitAcc0 = 1 << nStrAcc0;
const short bitReRedLght = 1 << nStrReRedLght;
const short bitBeacon = 1 << nStrBeacon;
const short bitLghtFrDown = 1 << nStrLghtFrDown;
const short bitLghtReDown = 1 << nStrLghtReDown;
const short bitRightBlinker = 1 << nStrRightBlinker;
const short bitLeftBlinker = 1 << nStrLeftBlinker;
const short bitWarning = 1 << nStrWarning;
const short bitLghtFrUp = 1 << nStrLghtFrUp;
const short bitLghtReUp = 1 << nStrLghtReUp;
volatile boolean sendOk = false;
boolean oldSendOk = false;
volatile boolean intOk = false;
// ----- keyboard chars to write
char kbWlist[] = { KEY_BACKSPACE, KEY_HOME, 'A', 'B', 'C', 'F', 'G', 'I', 'V', 'X', 'O', \
225, 226, 227, 229, 230, 0
};
volatile int kbCharW = 0;
const int bitKeyBS = 1 << 0;
const int bitKeyHome = 1 << 1;
const int bitKeyA = 1 << 2;
const int bitKeyB = 1 << 3;
const int bitKeyC = 1 << 4;
const int bitKeyF = 1 << 5;
const int bitKeyG = 1 << 6;
const int bitKeyI = 1 << 7;
const int bitKeyV = 1 << 8;
const int bitKeyX = 1 << 9;
const int bitKeyO = 1 << 10;
const int bitKP1 = 1 << 11;
const int bitKP2 = 1 << 12;
const int bitKP3 = 1 << 13;
const int bitKP5 = 1 << 14;
const int bitKP6 = 1 << 15;
const short nCharWList = 16;
// ----- keyboard chars to press/release
char kbPRlist[] = { KEY_PAGE_UP, KEY_PAGE_DOWN, '0', 0 };
volatile short kbCharPR = 0;
const short bitKeyPgUpPress = 1 << 0;
const short bitKeyPgUpRelease = 1 << 1;
const short bitKeyPgDwnPress = 1 << 2;
const short bitKeyPgDwnRelease = 1 << 3;
const short bitKey0Press = 1 << 4;
const short bitKey0Release = 1 << 5;
const short nCharPRList = 3; /// divided by 2 (set, released)
// ----- keyboard special send
volatile boolean kbCharSeqFFF = false;
volatile boolean kbCharSeqFFF5 = false;
volatile boolean kbCharSeqFFF6 = false;
volatile boolean kbCharSeq5F5 = false;
volatile boolean kbCharSeq6F6 = false;
volatile boolean kbCharSeq56F56 = false;
// index
short il;
unsigned int jl, kl, ll;
char *addStr;
// manage the time to send data (avoid overflow)
unsigned long t0, t1;
int dt = 100; // ms to send data to serial
boolean okToSend = false;
// verify if not blocked ...
unsigned long tHorn0;
int dtHorn = 5000; // ms max to be maintained
boolean forceReleaseButtons = false;
int releaseCnt = 0;
// ======================================================================================
// SETUP
// ======================================================================================
void setup() {
// -------------------------------------------- Serial (makes a reset !!)
Serial.begin(115200);
// -------------------------------------------- Strings to be sent
// firt letter : 0 = reset com, 1 = send to Disp, 2 = send both to Disp and Outputs
// then 3 letters, then value
strcpy(strToSend[nStrAcc0] , "1ACC0.00" ); // accel
strcpy(strToSend[nStrReRedLght] , "2RRL0" ); // ReRedLght
strcpy(strToSend[nStrBeacon] , "2BCN0" ); // Beacon
strcpy(strToSend[nStrLghtFrDown] , "2LFD0" ); // LghtFrDown
strcpy(strToSend[nStrLghtReDown] , "2LRD0" ); // LghtReDown
strcpy(strToSend[nStrRightBlinker], "2RBL0" ); // RightBlinker
strcpy(strToSend[nStrLeftBlinker] , "2LBL0" ); // LeftBlinker
strcpy(strToSend[nStrWarning] , "2WRN0" ); // Warning
strcpy(strToSend[nStrLghtFrUp] , "2LFU0" ); // LghtFrUp
strcpy(strToSend[nStrLghtReUp] , "2LRU0" ); // LghtReUp
// -------------------------------------------- Buttons
// ----- position N -> 'B'
attachInterrupt(pin_PosN_1, _posN, RISING);
attachInterrupt(pin_BP4_N, _posN, RISING);
// ----- Horn -> '0'
attachInterrupt(pin_Horn, _hornOn, RISING);
// ----- Beacon -> 'HOME'
attachInterrupt(pin_Beacon, _beacon, RISING);
// ----- action 1 -> 'O'
attachInterrupt(pin_SeqLev_PB, _act_o, RISING);
// ----- lights Front low -> KP5
attachInterrupt(pin_Li_FrDown, _lght_fr_low, RISING);
// ----- lights Rear low -> KP6
attachInterrupt(pin_Li_ReDown, _lght_re_low, RISING);
// ----- Right Blinker -> KP3
attachInterrupt(pin_RightBlinker, _rightBlinkOn, RISING);
// ----- Left Blinker -> KP1
attachInterrupt(pin_LeftBlinker, _leftBlinkOn, RISING);
// ----- Warning -> KP0
attachInterrupt(pin_Warning, _WarningOn, RISING);
// ----- light FR UP -> KP5
attachInterrupt(pin_Li_FrUp, _lght_fr_up, RISING);
// ----- light RE UP -> KP6
attachInterrupt(pin_Li_ReUp, _lght_re_up, RISING);
// ----- U-Turn -> 'G'
attachInterrupt(pin_UTurn, _u_turn, RISING);
// ----- LG_Up / LG_Down -> 'V'
attachInterrupt(pin_LG_Up, _lg_up, RISING);
attachInterrupt(pin_LG_Down, _lg_down, RISING);
// ----- HyLeftFwd -> 'X'
attachInterrupt(pin_HydrauLeftLeverFrwd, _hydLeftFwd, RISING);
attachInterrupt(pin_HydrauLeftLeverBkwd, _hydLeftBwd, RISING);
// ----- HyRightFwd -> 'I'
attachInterrupt(pin_HydrauRightLeverFrwd, _hydRightFwd, RISING);
attachInterrupt(pin_HydrauRightLeverBkwd, _hydRightBwd, RISING);
// ----- GreenButtonLeftJS -> 'C'
attachInterrupt(pin_GreenButtonLeftJS, _setCamera, RISING);
// ----- RedButtonLeftJS -> 'BACKSPACE'
attachInterrupt(pin_RedButtonLeftJS, _hideshowpan, RISING);
// ----- ZoomOut -> 'PgUp'
attachInterrupt(pin_GreenButtonRightJS, _zoomOutOn, RISING);
// ----- ZoomIN -> 'PgPgDwn'
attachInterrupt(pin_RedButtonRightJS, _zoomInOn, RISING);
// ----- Attach -> 'A'
attachInterrupt(pin_A, _attach, RISING);
// ----- Directions
attachInterrupt(pin_FrwdInfo, _forwardOn, RISING);
attachInterrupt(pin_BkwdInfo, _backwardOn, RISING);
attachInterrupt(pin_FrwdBkwdToggle, _toggleDir, RISING);
// -------------------------------------------- Analog inputs
analogReadResolution(12);
// -------------------------------------------- Analog outputs
analogWriteResolution(12);
analogWrite(out_forward, 0);
analogWrite(out_bckward, 0);
// ----- reset buffer
strcpy(rcvString, "");
// ----- wait for serial PC
while (!Serial);
// -------------------------------------------- keyboard
Keyboard.begin();
t0 = millis();
tHorn0 = t0;
}
// ======================================================================================
// SUBROUTINES
// ======================================================================================
// ----- send strings to the serial link
void sendSerial(char *txt) {
if ((sendOk) && (Serial.availableForWrite() > strlen(txt))) {
Serial.println(txt); // println add '\r\n' to the string
}
}
// ----- azerty to qwerty
char az2qw(char inch) {
char outch = inch;
switch (inch) {
case 'A': outch = 'Q'; break;
case 'Z': outch = 'W'; break;
case 'Q': outch = 'A'; break;
case 'W': outch = 'Z'; break;
case 'M': outch = ':'; break;
case 'a': outch = 'q'; break;
case 'z': outch = 'w'; break;
case 'q': outch = 'a'; break;
case 'w': outch = 'z'; break;
case 'm': outch = ';'; break;
break;
}
return outch;
}
// ----- set Direction
void setDirection(short val) {
Direction = val;
strToSend[nStrReRedLght][4] = char(48 + (val == -1 ? 1 : 0));
// redLights and reset speed
sndStrFlag |= (bitReRedLght | bitAcc0) ;
}
// 'ESC', dt, 'UP', dt, 'ENTER'
void quitFS17() {
delay(100);
Keyboard.write(KEY_ESC);
delay(200);
Keyboard.write(KEY_UP_ARROW);
delay(100);
Keyboard.write(KEY_RETURN);
delay(100);
}
// 'ESC', dt, 'RIGHT', dt, 'ENTER', dt, 'ENTER', dt
void quitGame() {
// mask the interruptions
intOk = false;
Keyboard.write(KEY_ESC);
delay(200);
Keyboard.write(KEY_RIGHT_ARROW);
delay(100);
Keyboard.write(KEY_RETURN);
delay(300);
Keyboard.write(KEY_RETURN);
delay(100);
}
// n -= 1 [ 'RIGHT', dt ]n , dt, 'ENTER', dt, 'ENTER'
void loadGame(short n) {
n -= 1;
if (n > 20) n = 20;
for (short ii = 0; ii < n; ii++) {
Keyboard.write(KEY_RIGHT_ARROW);
delay(100);
}
Keyboard.write(KEY_RETURN);
delay(150);
Keyboard.write(KEY_RETURN);
delay(100);
sndStrFlag |= bitAcc0;
}
// enter normal game
void simpleGame() {
Keyboard.write(KEY_BACKSPACE);
delay(200);
Keyboard.write(KEY_TAB);
delay(100);
}
// enter game with Harvest
void harvestGame() {
Keyboard.write(KEY_BACKSPACE);
delay(200);
Keyboard.write(KEY_TAB);
delay(200);
Keyboard.write('H');
delay(200);
Keyboard.write(KEY_TAB);
delay(100);
}
void AltTab() {
Keyboard.press(KEY_LEFT_ALT);
Keyboard.press(KEY_TAB);
delay(100);
Keyboard.releaseAll();
delay(100);
}
void ShiftAltTab() {
Keyboard.press(KEY_LEFT_SHIFT);
Keyboard.press(KEY_LEFT_ALT);
Keyboard.press(KEY_TAB);
delay(100);
Keyboard.releaseAll();
delay(100);
}
// personnal map (bug in the official one)
unsigned int myMap(float val, float minin, float maxin) {
return min((unsigned int) max(0., (val - minin) * (float)maxV12 / (maxin - minin)), maxV12);
}
// ======================================================================================
// MAIN LOOP
// ======================================================================================
void loop() {
// ------------------------------------------ commands read from serial input
while (Serial.available()) {
char inChar = (char)Serial.read();
rcvString[nrcv++] = inChar;
if (inChar == '\n') {
rcvString[strcspn(rcvString, "\r\n")] = 0;
// enFS, qtFS, qtGm, keyA, keyE, keyH, key{letter}, kPSC, kTAB, kENT, kBKS, kESC,
// ld{ngame:02d}, sOk0, sOk1
//---------------------------------------------------------------------------- keys
// ----- simple key
if (strncmp(rcvString, "key", 3) == 0) Keyboard.write(az2qw(rcvString[3]));
// ----- special key
else if (rcvString[0] == 'k') {
addStr = rcvString + 1 ;
if (rcvString[1] == 'F') {
if (strcmp(addStr, "F01") == 0) Keyboard.write(KEY_F1);
else if (strcmp(addStr, "F02") == 0) Keyboard.write(KEY_F2);
else if (strcmp(addStr, "F03") == 0) Keyboard.write(KEY_F3);
else if (strcmp(addStr, "F04") == 0) Keyboard.write(KEY_F4);
else if (strcmp(addStr, "F05") == 0) Keyboard.write(KEY_F5);
else if (strcmp(addStr, "F06") == 0) Keyboard.write(KEY_F6);
else if (strcmp(addStr, "F07") == 0) Keyboard.write(KEY_F7);
else if (strcmp(addStr, "F08") == 0) Keyboard.write(KEY_F8);
else if (strcmp(addStr, "F09") == 0) Keyboard.write(KEY_F9);
else if (strcmp(addStr, "F10") == 0) Keyboard.write(KEY_F10);
else if (strcmp(addStr, "F11") == 0) Keyboard.write(KEY_F11);
else if (strcmp(addStr, "F12") == 0) Keyboard.write(KEY_F12);
delay(100);
}
else if (rcvString[1] == 'K') {
if (strcmp(addStr, "KP0") == 0) Keyboard.write(KEYPAD_0);
else if (strcmp(addStr, "KP1") == 0) Keyboard.write(KEYPAD_1);
else if (strcmp(addStr, "KP2") == 0) Keyboard.write(KEYPAD_2);
else if (strcmp(addStr, "KP3") == 0) Keyboard.write(KEYPAD_3);
else if (strcmp(addStr, "KP4") == 0) Keyboard.write(KEYPAD_4);
else if (strcmp(addStr, "KP5") == 0) Keyboard.write(KEYPAD_5);
else if (strcmp(addStr, "KP6") == 0) Keyboard.write(KEYPAD_6);
else if (strcmp(addStr, "KP7") == 0) Keyboard.write(KEYPAD_7);
else if (strcmp(addStr, "KP8") == 0) Keyboard.write(KEYPAD_8);
else if (strcmp(addStr, "KP9") == 0) Keyboard.write(KEYPAD_9);
else if (strcmp(addStr, "KP/") == 0) Keyboard.write(KEYPAD_SLASH);
else if (strcmp(addStr, "KP*") == 0) Keyboard.write(KEYPAD_ASTERISK);
else if (strcmp(addStr, "KP-") == 0) Keyboard.write(KEYPAD_MINUS);
else if (strcmp(addStr, "KP+") == 0) Keyboard.write(KEYPAD_PLUS);
else if (strcmp(addStr, "KPE") == 0) Keyboard.write(KEYPAD_ENTER);
else if (strcmp(addStr, "KP.") == 0) Keyboard.write(KEYPAD_PERIOD);
delay(100);
}
else if (rcvString[1] == 'P') {
if (strcmp(addStr, "PSC") == 0) Keyboard.write(KEY_PRINT_SCREEN);
else if (strcmp(addStr, "PGU") == 0) Keyboard.write(KEY_PAGE_UP);
else if (strcmp(addStr, "PGD") == 0) Keyboard.write(KEY_PAGE_DOWN);
else if (strcmp(addStr, "PAU") == 0) Keyboard.write(KEY_PAUSE);
delay(100);
}
else if (rcvString[1] == 'E') {
if (strcmp(addStr, "ESC") == 0) Keyboard.write(KEY_ESC);
else if (strcmp(addStr, "END") == 0) Keyboard.write(KEY_END);
else if (strcmp(addStr, "ENT") == 0) Keyboard.write(KEYPAD_ENTER);
delay(100);
}
else if (rcvString[1] == 'A') {
if (strcmp(addStr, "ArU") == 0) Keyboard.write(KEY_UP_ARROW);
else if (strcmp(addStr, "ArD") == 0) Keyboard.write(KEY_DOWN_ARROW);
else if (strcmp(addStr, "ArL") == 0) Keyboard.write(KEY_LEFT_ARROW);
else if (strcmp(addStr, "ArR") == 0) Keyboard.write(KEY_RIGHT_ARROW);
delay(100);
}
else {
if (strcmp(addStr, "TAB") == 0) Keyboard.write(KEY_TAB);
else if (strcmp(addStr, "RET") == 0) Keyboard.write(KEY_RETURN);
else if (strcmp(addStr, "BKS") == 0) Keyboard.write(KEY_BACKSPACE);
else if (strcmp(addStr, "HOM") == 0) Keyboard.write(KEY_HOME);
delay(100);
}
}
// ----- combined keys
else if (strcmp(rcvString, "AlTb") == 0) AltTab();
else if (strcmp(rcvString, "ShAT") == 0) ShiftAltTab();
// ---------------------------------------------------------------------- functions
// ----- send info or not
else if (strncmp(rcvString, "sOk", 3) == 0) sendOk = (rcvString[3] == '1');
// ----- accept or not interrupt
else if (strncmp(rcvString, "iOk", 3) == 0) intOk = (rcvString[3] == '1');
// ----- load game #
else if (strncmp(rcvString, "ld", 2) == 0) loadGame(atoi(rcvString + 2));
// ----- simple game
else if (strcmp(rcvString, "SiGa") == 0) simpleGame();
// ----- harvest game
else if (strcmp(rcvString, "HaGa") == 0) harvestGame();
// ----- quit FS
else if (strcmp(rcvString, "qtGm") == 0) {
quitGame();
forceReleaseButtons = true;
}
// ----- quit FS
else if (strcmp(rcvString, "relb") == 0) forceReleaseButtons = true;
// ----- reset Arduino Due
else if (strcmp(rcvString, "rstd") == 0) RSTC->RSTC_CR = 0xA5000005;
// ----- clear string
strcpy(rcvString, "");
nrcv = 0;
}
}
// ----- reset speed
if (sndStrFlag & bitAcc0) {
analogWrite(out_forward, 0);
analogWrite(out_bckward, 0);
}
// ----- Common loop PR/ PC / keyboard (nCharPRList < nStrToSend < nCharWList)
for (il = 0, jl = 1, kl = 1, ll = 2; il < nCharPRList; il++, jl <<= 1, kl <<= 2, ll <<= 2) {
if (kbCharPR & kl) {
// press key
Keyboard.press(kbPRlist[il]);
kbCharPR &= ~kl;
}
else if ((kbCharPR & ll)) {
// release key
Keyboard.release(kbPRlist[il]);
kbCharPR &= ~ll;
}
if (sndStrFlag & jl) {
// send Strings to PC
sendSerial(strToSend[il]);
sndStrFlag &= ~jl;
}
if (kbCharW & jl) {
// chars to send (write) to keyboard
Keyboard.write(kbWlist[il]);
kbCharW &= ~jl;
}
}
// ----- Common loop PC / keyboard (nStrToSend < nCharWList)
for (; il < nStrToSend; il++, jl <<= 1) {
if (sndStrFlag & jl) {
// send Strings to PC
sendSerial(strToSend[il]);
sndStrFlag &= ~jl;
}
if (kbCharW & jl) {
// chars to send (write) to keyboard
Keyboard.write(kbWlist[il]);
kbCharW &= ~jl;
}
}
// ----- remaining chars to write to keyboard
for (; il < nCharWList; il++, jl <<= 1) {
if (kbCharW & jl) {
Keyboard.write(kbWlist[il]);
kbCharW &= ~jl;
}
}
// ----- special sequence to send to keyboard
if (kbCharSeqFFF) {
for (il = 0; il < 3; il++) Keyboard.write('F');
kbCharSeqFFF = false;
}
if (kbCharSeqFFF5) {
for (il = 0; il < 3; il++) Keyboard.write('F');
delay(100);
Keyboard.write(229);
kbCharSeqFFF5 = false;
}
if (kbCharSeqFFF6) {
for (il = 0; il < 3; il++) Keyboard.write('F');
delay(100);
Keyboard.write(230);
kbCharSeqFFF6 = false;
}
if (kbCharSeq5F5) {
Keyboard.write(229);
delay(100);
Keyboard.write('F');
delay(100);
Keyboard.write(229);
kbCharSeq5F5 = false;
}
if (kbCharSeq6F6) {
Keyboard.write(230);
delay(100);
Keyboard.write('F');
delay(100);
Keyboard.write(230);
kbCharSeq6F6 = false;
}
if (kbCharSeq56F56) {
Keyboard.write(229);
delay(100);
Keyboard.write(230);
delay(100);
Keyboard.write('F');
delay(100);
Keyboard.write(229);
delay(100);
Keyboard.write(230);
kbCharSeq56F56 = false;
}
if (forceReleaseButtons) {
// release all buttons
oldSendOk = sendOk;
sendOk = true;
if (hornOn) _hornOff();
if (beaconOn) _beacon();
if (warningOn) _WarningOff();
if (Li_fr_down) _lght_fr_low();
if (Li_re_down) _lght_re_low();
if (Li_fr_up) _lght_fr_up();
if (Li_re_up) _lght_re_up();
if (leftBlinkerOn) _leftBlinkOff();
if (rightBlinkerOn) _rightBlinkOff();
if (zoomInOn) _zoomInOff();
if (zoomOutOn) _zoomOutOff();
if (cameraOn) _setCamera();
forceReleaseButtons = false;
releaseCnt = 1;
}
// check delta time to send data
t1 = millis();
okToSend = ((unsigned long)(t1 - t0) >= dt); // cast to avoid to manage roll-over
// check if horn not blocked
if (hornOn && ((unsigned long)(t1 - tHorn0) >= dtHorn)) forceReleaseButtons = true;
// ------------------------------------------ Steering Wheel
// read from ADC (0.1 to 3.2)
curStwVal = myMap(analogRead(in_steeringW), 124, 3971);
// write to DAC
if (curStwVal != prevStwVal) {
analogWrite(out_steeringW, curStwVal);
prevStwVal = curStwVal;
curStwAngle = map(curStwVal, 0, maxV12, -30, 30);
if ((curStwAngle != prevStwAngle) && (okToSend)) {
sprintf(sndString, "1STW%d", curStwAngle); // + offset ?
sendSerial(sndString);
prevStwAngle = curStwAngle;
}
}
// ------------------------------------------ Accel
// read from ADC (accel pedal max value is 2.4 V instead of 3.2)
if ((!brakesOn) && (Direction != 0)) {
accelPedal = myMap(analogRead(in_acc_pedal), 124, 2966);
accelHand = myMap(analogRead(in_acc_hand), 124, 2225);
curAccel = max(accelPedal, accelHand);
// write to DAC
if (curAccel != prevAccel) {
prevAccel = curAccel;
if (Direction == 1) analogWrite(out_forward, curAccel);
else if (Direction == -1) analogWrite(out_bckward, curAccel);
curAccel2 = map(curAccel, 0, maxV12, 0, 100);
if ((curAccel2 != prevAccel2) && (okToSend)) {
sprintf(sndString, "1ACC%.2f", min(1.05 * float(curAccel2) / 100., 1.));
sendSerial(sndString);
prevAccel2 = curAccel2;
}
}
}
// ------------------------------------------ brakes
curBrksVal = myMap(analogRead(in_brakes), 124, 3971);
if (curBrksVal != prevBrksVal) {
prevBrksVal = curBrksVal;
analogWrite(out_brakes, curBrksVal);
if ((curBrksVal < 820) && (brakesOn)) {
sendSerial((char *)"2BRK0");
brakesOn = false;
}
else if ((curBrksVal > 1230) && (!brakesOn)) {
sendSerial((char *)"2BRK1");
// reset speed
sndStrFlag |= bitAcc0;
brakesOn = true;
}
}
if (okToSend) t0 = t1;
if (releaseCnt) {
// count the nb of loop: be sure we made 1 loop at least
releaseCnt += 1;
if (releaseCnt == 3) {
// we are sure here that the buttons were released
releaseCnt = 0;
sendOk = oldSendOk;
}
}
}
// ======================================================================================
// INTERRUPTIONS
// ======================================================================================
// ---------------------------------------------- pin_PosN_0 & pin_PosN_1 & pin_BP4_N
void _posN() {
if (intOk) kbCharW |= bitKeyB; // PosN -> 'B'
}
// ---------------------------------------------- pin_Horn
void _hornOn() {
if (intOk) {
attachInterrupt(pin_Horn, _hornOff, FALLING);
hornOn = true;
tHorn0 = millis();
kbCharPR |= bitKey0Press; // Horn -> '0' HOLD
}
}
void _hornOff() {
kbCharPR |= bitKey0Release; // Horn -> '0' RELEASE
hornOn = false;
attachInterrupt(pin_Horn, _hornOn, RISING);
}
// ---------------------------------------------- pin_Beacon
void _beacon() {
if (intOk) {
kbCharW |= bitKeyHome ; // 'HOME'
beaconOn = !beaconOn;
strToSend[nStrBeacon][4] = char(48 + int(beaconOn));
sndStrFlag |= bitBeacon;
}
}
// ---------------------------------------------- pin_SeqLev_PB
void _act_o() {
if (intOk) kbCharW |= bitKeyO; // action -> 'O'
}
// ---------------------------------------------- pin_RightBlinker
void _rightBlinkOn() {
if (intOk) {
attachInterrupt(pin_RightBlinker, _rightBlinkOff, FALLING);
kbCharW |= bitKP3; // KP3 -> 227
strToSend[nStrRightBlinker][4] = '1';
rightBlinkerOn = true;
sndStrFlag |= bitRightBlinker;
}
}
void _rightBlinkOff() {
kbCharW |= bitKP3; // KP3 -> 227
strToSend[nStrRightBlinker][4] = '0';
sndStrFlag |= bitRightBlinker;
rightBlinkerOn = false;
attachInterrupt(pin_RightBlinker, _rightBlinkOn, RISING);
}
// ---------------------------------------------- pin_LeftBlinker
void _leftBlinkOn() {
if (intOk) {
attachInterrupt(pin_LeftBlinker, _leftBlinkOff, FALLING);
kbCharW |= bitKP1; // KP1 -> 225
strToSend[nStrLeftBlinker][4] = '1';
leftBlinkerOn = true;
sndStrFlag |= bitLeftBlinker;
}
}
void _leftBlinkOff() {
kbCharW |= bitKP1; // KP1 -> 225
strToSend[nStrLeftBlinker][4] = '0';
sndStrFlag |= bitLeftBlinker;
leftBlinkerOn = false;
attachInterrupt(pin_LeftBlinker, _leftBlinkOn, RISING);
}
// ---------------------------------------------- pin_Warning
void _WarningOn() {
if (intOk) {
attachInterrupt(pin_Warning, _WarningOff, FALLING);
kbCharW |= bitKP2; // KP2 -> 226
strToSend[nStrWarning][4] = '1';
warningOn = true;
sndStrFlag |= bitWarning;
}
}
void _WarningOff() {
kbCharW |= bitKP2; // KP2 -> 226
strToSend[nStrWarning][4] = '0';
sndStrFlag |= bitWarning;
warningOn = false;
attachInterrupt(pin_Warning, _WarningOn, RISING);
}
// ---------------------------------------------- pin_Li_FrUp
void _lght_fr_up() {
if (intOk) {
Li_fr_up = !Li_fr_up;
strToSend[nStrLghtFrUp][4] = char('0' + int(Li_fr_up));
sndStrFlag |= bitLghtFrUp;
kbCharW |= bitKP5; // KP5 -> 229
}
}
// ---------------------------------------------- pin_Li_FrDown
void _lght_fr_low() {
if (intOk) {
Li_fr_down = !Li_fr_down;
strToSend[nStrLghtFrUp][4] = char('0' + int(Li_fr_up));
sndStrFlag |= bitLghtFrDown;
boolean Li_re = Li_re_up || Li_re_down;
if (Li_fr_down) {
if (Li_fr_up && Li_re) kbCharSeq56F56 = true;
else if (Li_fr_up) kbCharSeq5F5 = true;
else if (Li_re) kbCharSeq6F6 = true;
else kbCharW |= bitKeyF;
}
else {
if (Li_fr_up != Li_re) kbCharSeqFFF = true;
else if (Li_fr_up) kbCharSeqFFF5 = true;
else kbCharSeqFFF6 = true;
}
}
}
// ---------------------------------------------- pin_Li_ReUp
void _lght_re_up() {
if (intOk) {
Li_re_up = !Li_re_up;
strToSend[nStrLghtReUp][4] = char('0' + int(Li_re_up));
sndStrFlag |= bitLghtReUp;
if (! Li_re_down) kbCharW |= bitKP6; // KP6 -> 230
}
}
// ---------------------------------------------- pin_Li_ReDown
void _lght_re_low() {
if (intOk) {
Li_re_down = !Li_re_down;
strToSend[nStrLghtReUp][4] = char('0' + int(Li_re_up));
sndStrFlag |= bitLghtReDown;
if (! Li_re_up) kbCharW |= bitKP6; // KP6 -> 230
}
}
// ---------------------------------------------- pin_UTurn
void _u_turn() {
if (intOk) kbCharW |= bitKeyG; // 'G'
}
// ---------------------------------------------- pin_LG_Up
void _lg_up() {
if ((intOk) && (!Lg_up)) {
kbCharW |= bitKeyV; // 'V'
Lg_up = true;
}
}
// ---------------------------------------------- pin_LG_Down
void _lg_down() {
if ((intOk) && (Lg_up)) {
kbCharW |= bitKeyV; // 'V'
Lg_up = false;
}
}
// ---------------------------------------------- pin_HydrauLeftLeverFrwd
void _hydLeftFwd() {
if (intOk) kbCharW |= bitKeyX; // 'X'
}
// ---------------------------------------------- pin_HydrauLeftLeverBkwd
void _hydLeftBwd() {
if (intOk) kbCharW |= bitKeyX; // 'X'
}
// ---------------------------------------------- pin_HydrauRightLeverFrwd
void _hydRightFwd() {
if (intOk) kbCharW |= bitKeyI; // 'I'
}
// ---------------------------------------------- pin_HydrauRightLeverBkwd
void _hydRightBwd() {
if (intOk) kbCharW |= bitKeyI; // 'I'
}
// ---------------------------------------------- pin_GreenButtonLeftJS
void _setCamera() {
cameraOn = !cameraOn;
if (intOk) kbCharW |= bitKeyC; // 'C'
}
// ---------------------------------------------- pin_RedButtonLeftJS
void _hideshowpan() {
if (intOk) kbCharW |= bitKeyBS; // 'BS'
}
// ---------------------------------------------- pin_GreenButtonRightJS
void _zoomOutOn() {
if (intOk) {
attachInterrupt(pin_GreenButtonRightJS, _zoomOutOff, FALLING);
zoomOutOn = true;
kbCharPR |= bitKeyPgUpPress; // zoomOut -> 'PgUp' HOLD
}
}
void _zoomOutOff() {
kbCharPR |= bitKeyPgUpRelease; // zoomOut -> 'PgUp' RELEASE
zoomOutOn = false;
attachInterrupt(pin_GreenButtonRightJS, _zoomOutOn, RISING);
}
// ---------------------------------------------- pin_RedButtonRightJS
void _zoomInOn() {
if (intOk) {
attachInterrupt(pin_RedButtonRightJS, _zoomInOff, FALLING);
zoomInOn = true;
kbCharPR |= bitKeyPgDwnPress; // zoomIn -> 'PgDown' HOLD
}
}
void _zoomInOff() {
kbCharPR |= bitKeyPgDwnRelease; // zoomOut -> 'PgDown' RELEASE
zoomInOn = false;
attachInterrupt(pin_RedButtonRightJS, _zoomInOn, RISING);
}
// ---------------------------------------------- pin_FrwdInfo
void _forwardOn() {
attachInterrupt(pin_FrwdInfo, _forwardOff, FALLING);
setDirection(1);
}
void _forwardOff() {
setDirection(0);
attachInterrupt(pin_FrwdInfo, _forwardOn, RISING);
}
// ---------------------------------------------- pin_BkwdInfo
void _backwardOn() {
attachInterrupt(pin_BkwdInfo, _backwardOff, FALLING);
setDirection(-1);
}
void _backwardOff() {
setDirection(0);
attachInterrupt(pin_BkwdInfo, _backwardOn, RISING);
}
// ---------------------------------------------- pin_FrwdBkwdToggle
void _toggleDir() {
if (Direction == 0) Direction = -1 ;
setDirection(-Direction);
}
// ---------------------------------------------- pin_A
void _attach() {
if (intOk) kbCharW |= bitKeyA; // 'A'
}
__________________________________________________________________________
2/ Arduino UNO (attached to "Master" PC)
Nothing very special to notice... It's a quite simple program.
inputsUNO.c _______________________________________________________________
/*
Program Arduino UNO
Manage relays for Farming Simulator
Damien Hallez, nov 2017
*/
// ----- pins for Raspberry's
const short RPi_Out = 0;
const short RPi_Disp = 1;
const short RPi_Pres = 2;
const short Supply15 = 3;
short Rpi_pins[] = {10, 11, 12, 13};
// ----- Sound relays (high current)
short Sound_pins[] = {8, 9};
// pin for reset DUE
const short pin_resetDue = 3;
// ----- Serial -> pins 18 & 19
// Cylinders orders
const short Order1 = 0;
const short Order2 = 1;
const short Order3 = 2;
const short Order4 = 3;
short Order_pins[] = {7, 6, 5, 4};
const short pin_TireUp = 2 ; // ---- Tire Up info
// ----- boolean to send
volatile boolean sendTireUp = false;
char rcvString[10] ;
int nrcv = 0;
// ======================================================================================
// SETUP
// ======================================================================================
void setup() {
// -------------------------------------------- Serial (makes a reset !!)
Serial.begin(115200);
// -------------------------------------------- Relays
// ----- output pins for sound relays
for (short i=0; i<2; i++) pinMode(Sound_pins[i], OUTPUT);
sndRelays(0);
// ----- output pins for Rpi's relays
for (short i=0; i<4; i++) pinMode(Rpi_pins[i], OUTPUT);
rpiRelays('a', 0);
setSupply(1);
// ----- output pins for cylinders orders' relays
for (short i=0; i<4; i++) pinMode(Order_pins[i], OUTPUT);
orderRelays(0);
// reset DUE
pinMode(pin_resetDue, OUTPUT);
digitalWrite(pin_resetDue, HIGH);
// attach an interrupt to the tire Up info
attachInterrupt(digitalPinToInterrupt(pin_TireUp), _TireUp, RISING);
// ----- wait for serial PC
while (!Serial);
}
// ======================================================================================
// SUBROUTINES
// ======================================================================================
// ----- send strings to the serial link
void sendSerial(char *txt) {
if (Serial.availableForWrite() > strlen(txt)) {
Serial.println(txt); // println add '\r\n' to the string
}
}
// ----- relays sound
void sndRelays(short onOff) {
for (short i=0; i<2; i++) digitalWrite(Sound_pins[i], onOff);
}
// ----- relays RPi's
void rpiRelays(char nr, short onOff) {
if (nr == 'a') {
for (short i=0; i<3; i++) digitalWrite(Rpi_pins[i], 1-onOff);
}
else digitalWrite(Rpi_pins[nr-'0'], 1-onOff);
}
// ----- set 15v supply
void setSupply(short onOff) {
digitalWrite(Rpi_pins[Supply15], 1-onOff);
}
// ----- relays orders
void orderRelays(short nr) {
if (nr == 0){
for (short i=0; i<4; i++) digitalWrite(Order_pins[i], 1);
}
else {
for (short i=1; i<5; i++) {
if (i == nr) digitalWrite(Order_pins[i-1], 0);
else digitalWrite(Order_pins[i-1], 1);
}
}
}
void resetDue() {
digitalWrite(pin_resetDue, LOW);
delay(500);
digitalWrite(pin_resetDue, HIGH);
}
// ======================================================================================
// MAIN LOOP
// ======================================================================================
void loop() {
// ------------------------------------------ commands read from serial input
while (Serial.available()) {
char inChar = (char)Serial.read();
rcvString[nrcv++] = inChar;
if (inChar == '\n') {
rcvString[strcspn(rcvString, "\r\n")] = 0;
// for DEBUG Serial.println(rcvString);
// snd{0|1}, rpi{0|1|2|a}{0|1}, rstd, ord{0|1|2|3}, sup{0|1}
if (strncmp(rcvString, "snd", 3) == 0) sndRelays(rcvString[3] - '0');
else if (strncmp(rcvString, "rpi", 3) == 0) rpiRelays(rcvString[3], rcvString[4] - '0');
else if (strncmp(rcvString, "sup", 3) == 0) setSupply(rcvString[3] - '0');
else if (strncmp(rcvString, "ord", 3) == 0) orderRelays(rcvString[3] - '0');
else if (strcmp(rcvString, "rstd") == 0) resetDue();
else if (strcmp(rcvString, "rstu") == 0) {
setSupply(1);
sndRelays(0);
rpiRelays('a', 1);
orderRelays(0);
}
else if (strcmp(rcvString, "shtd") == 0) {
sndRelays(0);
rpiRelays('a', 0);
orderRelays(0);
setSupply(0);
}
strcpy(rcvString, "");
nrcv = 0;
}
}
if (sendTireUp) {
sendSerial((char *)"TiUp");
sendTireUp = false;
}
}
// ======================================================================================
// INTERRUPTIONS
// ======================================================================================
// ---------------------------------------------- pin_PosN_0 & pin_PosN_1 & pin_BP4_N
void _TireUp() {
sendTireUp = true; // set flag to true
}
__________________________________________________________________________
Back to intro
Tractor Simulator (interface with Farming Simulator 17)
Global presentation
Tractor simulator (cabin tractor interface with Farming Simulator 17): Part 1 - global presentation
The hardware
Tractor simulator (cabin tractor interface with Farming Simulator 17): Part 2 - the hardwareTractor simulator (cabin tractor interface with Farming Simulator 17): Part 2 - the hardware
Actual doc
Raspberry Pis
PCs
Tractor simulator (cabin tractor interface with Farming Simulator 17): Part 3 - the software, 3/ PCs