Considero que funciona ya lo suficientemente bien como para publicar aquí todo:
Transmisor - se ejecuta en el Arduino:
#define ARRAY_SIZE(x) sizeof(x) / sizeof(x[0])
static const unsigned int ANALOGIC_RES_VIRTUAL = 100;
static const unsigned int ANALOGIC_RES_PHYSICAL = 1024;
static const float ANALOGIC_RES_RATIO = (float) ANALOGIC_RES_VIRTUAL / (float) ANALOGIC_RES_PHYSICAL;
static const unsigned int ANALOGIC_THRESHOLD = ceil(ANALOGIC_RES_PHYSICAL / ANALOGIC_RES_VIRTUAL);
static const unsigned int TIMEMIN = 10;
static const unsigned int TIMEMAX = 1000; // milliseconds
typedef struct {
int pin;
boolean analogic;
} Port;
static const Port PORT[] = {
{ A0, true },
{ A1, true },
{ 2, false },
{ 3, false },
};
typedef struct {
unsigned int lastvalue;
unsigned long lasttime;
} PortStatus;
static PortStatus PORT_STATUS[ARRAY_SIZE(PORT)];
static boolean forceUpdate;
void setup() {
Serial.begin(115200);
for (int i = 0; i < ARRAY_SIZE(PORT); i++) {
if (!PORT[i].analogic) {
digitalWrite(PORT[i].pin, HIGH); // Enable pull-up
}
}
forceUpdate = true;
}
void loop() {
unsigned long now = millis();
for (int i = 0; i < ARRAY_SIZE(PORT); i++) {
boolean update = forceUpdate;
unsigned char outputValue;
if (!update) {
unsigned long passed = now - PORT_STATUS[i].lasttime;
if (passed <= TIMEMIN) continue;
update = (passed >= TIMEMAX);
}
if (PORT[i].analogic) {
unsigned int analogValue = analogRead(PORT[i].pin);
if (!update) {
update = (abs(analogValue - PORT_STATUS[i].lastvalue) >= ANALOGIC_THRESHOLD);
}
if (update) {
outputValue = round((float) analogValue * ANALOGIC_RES_RATIO);
PORT_STATUS[i].lastvalue = analogValue;
}
} else {
outputValue = !digitalRead(PORT[i].pin); // Not because active-low
if (!update) {
update = (PORT_STATUS[i].lastvalue != outputValue);
}
if (update) {
PORT_STATUS[i].lastvalue = outputValue;
}
}
if (update) {
Serial.write(0x80 + i);
Serial.write(outputValue);
PORT_STATUS[i].lasttime = now;
}
}
forceUpdate = false;
}
Receptor, ejecutándose en el PC. Programado en el lenguaje de prototipado AutoIT3:
#include <WinAPI.au3>
#include <ProcessConstants.au3>
#include "CommInterface.au3"
Func __ReadProcessData($hProcess, $iAddress, $sWhat)
Local $hBuffer = DllStructCreate($sWhat)
Local $iRead, $iSize
$iSize = DllStructGetSize($hBuffer)
If Not _WinAPI_ReadProcessMemory($hProcess, $iAddress, DllStructGetPtr($hBuffer), $iSize, $iRead) Or $iSize <> $iRead Then
SetError(1)
Return False
EndIf
Return DllStructGetData($hBuffer, 1)
EndFunc
Func __WriteProcessData($hProcess, $iAddress, $sWhat, $sData)
Local $hBuffer = DllStructCreate($sWhat)
Local $iRead, $iSize
DllStructSetData($hBuffer, 1, $sData)
$iSize = DllStructGetSize($hBuffer)
If Not _WinAPI_WriteProcessMemory($hProcess, $iAddress, DllStructGetPtr($hBuffer), $iSize, $iRead) Or $iSize <> $iRead Then
SetError(1)
Return False
EndIf
Return True
EndFunc
Func __ReadProcessPtrs($hProcess, $iAddresses)
Local $i
Local $iPtr = $iAddresses[0]
For $i = 1 To UBound($iAddresses) - 1
$iPtr = __ReadProcessData($hProcess, $iPtr, "ptr")
If @error Or $iPtr = 0 Then
SetError(1)
Return False
EndIf
$iPtr += $iAddresses[$i]
Next
Return $iPtr
EndFunc
Func __ReadThrottlePtr($hProcess)
Local Const $iOffsets[4] = [0x7C8778, 0x72, 0x8, 0x8C] ; 8C+[8+[72+[7C8778]]]
Local $iPtr
$iPtr = __ReadProcessPtrs($hProcess, $iOffsets)
If @error Then
SetError(1)
Return False
EndIf
Return $iPtr
EndFunc
Func ReadThrottle($hProcess)
Local $iPtr, $fThrottle
$iPtr = __ReadThrottlePtr($hProcess)
If @error Then
SetError(1)
Return False
EndIf
$fThrottle = __ReadProcessData($hProcess, $iPtr, "float")
If @error Then
SetError(2)
Return False
EndIf
Return $fThrottle
EndFunc
Func WriteThrottle($hProcess, $fThrottle)
Local $iPtr
$iPtr = __ReadThrottlePtr($hProcess)
If @error Then
SetError(1)
Return False
EndIf
ConsoleWrite(hex($iPtr, 8) & @crlf)
__WriteProcessData($hProcess, $iPtr, "float", $fThrottle)
If @error Then
SetError(2)
Return False
EndIf
Return True
EndFunc
Func __ReadBrakePtr($hProcess)
Local Const $iOffsets[4] = [0x7C8778, 0x72, 0x8, 0x128] ; 8C+[8+[72+[7C8778]]]
Local $iPtr
$iPtr = __ReadProcessPtrs($hProcess, $iOffsets)
If @error Then
SetError(1)
Return False
EndIf
Return $iPtr
EndFunc
Func WriteBrake($hProcess, $fThrottle)
Local $iPtr, $fSpeed
$iPtr = __ReadBrakePtr($hProcess)
If @error Then
SetError(1)
Return False
EndIf
__WriteProcessData($hProcess, $iPtr, "float", $fThrottle)
If @error Then
SetError(2)
Return False
EndIf
Return True
EndFunc
Func __ReadInverterModePtr($hProcess)
Local Const $iOffsets[4] = [0x7C8778, 0x72, 0x8, 0xB8] ; 8C+[8+[72+[7C8778]]]
Local $iPtr
$iPtr = __ReadProcessPtrs($hProcess, $iOffsets)
If @error Then
SetError(1)
Return False
EndIf
Return $iPtr
EndFunc
Func __ReadInverterMultiplierPtr($hProcess)
Local Const $iOffsets[4] = [0x7C8778, 0x72, 0x8, 0xCA] ; 8C+[8+[72+[7C8778]]]
Local $iPtr
$iPtr = __ReadProcessPtrs($hProcess, $iOffsets)
If @error Then
SetError(1)
Return False
EndIf
Return $iPtr
EndFunc
Func WriteInverter($hProcess, $iInverter)
Local $iPtr, $iMultiplier
$iPtr = __ReadInverterModePtr($hProcess)
If @error Then
SetError(1)
Return False
EndIf
__WriteProcessData($hProcess, $iPtr, "byte", $iInverter)
If @error Then
SetError(2)
Return False
EndIf
$iPtr = __ReadInverterMultiplierPtr($hProcess)
If @error Then
SetError(1)
Return False
EndIf
Switch $iInverter
Case 2
$iMultiplier = 0x3F80
case 1
$iMultiplier = 0
case 0
$iMultiplier = 0xBF80
EndSwitch
__WriteProcessData($hProcess, $iPtr, "ushort", $iMultiplier)
If @error Then
SetError(2)
Return False
EndIf
Return True
EndFunc
Func __ReadHornPtr($hProcess)
Local Const $iOffsets[4] = [0x7C8778, 0x72, 0x8, 0x1E0]
Local $iPtr
$iPtr = __ReadProcessPtrs($hProcess, $iOffsets)
If @error Then
SetError(1)
Return False
EndIf
Return $iPtr
EndFunc
Func WriteHorn($hProcess, $fHorn)
$iPtr = __ReadHornPtr($hProcess)
If @error Then
SetError(1)
Return False
EndIf
__WriteProcessData($hProcess, $iPtr, "float", $fHorn)
If @error Then
SetError(2)
Return False
EndIf
Return True
EndFunc
$hSerial = _CommAPI_OpenCOMPort(2, 115200, "N", 8, 0)
If @error Then
ConsoleWrite("Cannot open serial")
Exit(1)
EndIf
_CommAPI_ClearCommError($hSerial)
$iPIDs = ProcessList("train.exe")
If $iPIDs[0][0] = 0 Then
ConsoleWrite("Cannot find running TRAIN.EXE")
Exit(2)
EndIf
$hProcess = _WinAPI_OpenProcess($PROCESS_VM_READ + $PROCESS_VM_WRITE + $PROCESS_VM_OPERATION, False, $iPIDs[1][1])
If @error Or Not $hProcess Then
ConsoleWrite("Cannot open process")
Exit(2)
EndIf
While True
$iCode = _CommAPI_ReceiveBinary($hSerial, -1, 1)
If @error Then
ConsoleWriteError("Cannot read from serial")
ExitLoop
EndIf
$iCode = Asc($iCode)
If $iCode >= 0x80 Then
Switch $iCode
Case 0x80
$iCode = _CommAPI_ReceiveBinary($hSerial, -1, 1)
If @error Then
ConsoleWriteError("Cannot read from serial")
ExitLoop
EndIf
$iCode = Asc($iCode)
if ($iCode <= 100) Then
WriteThrottle($hProcess, $iCode / 100.0)
EndIf
Case 0x81
$iCode = _CommAPI_ReceiveBinary($hSerial, -1, 1)
If @error Then
ConsoleWriteError("Cannot read from serial")
ExitLoop
EndIf
$iCode = Asc($iCode)
if ($iCode <= 100) Then
WriteBrake($hProcess, $iCode / 100.0)
EndIf
Case 0x82
$iCode = _CommAPI_ReceiveBinary($hSerial, -1, 1)
If @error Then
ConsoleWriteError("Cannot read from serial")
ExitLoop
EndIf
$fThrottle = readthrottle($hprocess)
WriteThrottle($hprocess, 0)
if $iCode <> Chr(0) Then
WriteInverter($hprocess, 2)
Else
WriteInverter($hprocess, 1)
EndIf
WriteThrottle($hprocess, $fThrottle)
Case 0x83
$iCode = _CommAPI_ReceiveBinary($hSerial, -1, 1)
If @error Then
ConsoleWriteError("Cannot read from serial")
ExitLoop
EndIf
if $iCode <> Chr(0) Then
WriteHorn($hProcess, 1.0)
Else
WriteHorn($hProcess, 0.0)
EndIf
Case Else
ConsoleWriteError("Illegal command byte: " & Hex($iCode, 2) & @CRLF)
EndSwitch
EndIf
WEnd
Éste último requiere la librería CommAPI (
http://www.autoitscript.com/wiki/CommAPI) y ejecutarse como administrador (para poder acceder a la memoria de otro proceso) una vez que el MSTS se está ya ejecutando.