Semana 4¶
Esta semana analizaremos una posible solución al ejercicio planteado y realizaremos la primera evaluación sumativa.
Una posible solución al ejercicio¶
La solución al ejercicio se compone del código de Arduino para simular el sensor y el código de Unity para leer los datos del sensor.
Código de Arduino¶
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 | #include <Arduino.h>
//#define DEBUG
#ifdef DEBUG
#define DEBUG_PRINT(msg,value) Serial.print(msg); Serial.println(value)
#else
#define DEBUG_PRINT(msg,value)
#endif
void TaskReadCommand();
unsigned int uiCrc16Cal(unsigned char const *, unsigned char);
void parseCommad(uint8_t *);
void setup()
{
Serial.begin(57600);
}
void loop()
{
TaskReadCommand();
}
void TaskReadCommand()
{
enum class serialStates{
waitLen,
waitData
};
static auto state = serialStates::waitLen;
static uint8_t buffer[32] = {0};
static uint8_t dataCounter = 0;
switch (state)
{
case serialStates::waitLen: // wait for the first byte: len
if (Serial.available())
{
buffer[dataCounter] = Serial.read();
dataCounter++;
state = serialStates::waitData;
DEBUG_PRINT("Go to rx data", "");
}
break;
case serialStates::waitData: // read data
while (Serial.available())
{
buffer[dataCounter] = Serial.read();
dataCounter++;
if (dataCounter == (buffer[0] + 1))
{ // if all bytes arrived
// verify the checksum
DEBUG_PRINT("Verify the checksum", "");
DEBUG_PRINT("dataCount: ", dataCounter);
if (dataCounter >= 5)
{
unsigned int checksum = uiCrc16Cal(buffer, dataCounter - 2);
uint8_t lsBChecksum = (uint8_t)(checksum & 0x000000FF);
uint8_t msBChecksum = (uint8_t)((checksum & 0x0000FF00) >> 8);
if ((lsBChecksum == buffer[dataCounter - 2]) && (msBChecksum == buffer[dataCounter - 1]))
{
DEBUG_PRINT("ChecksumOK", "");
parseCommad(buffer);
}
}
dataCounter = 0;
state = serialStates::waitLen;
DEBUG_PRINT("Go to rx len", "");
}
}
break;
}
}
void parseCommad(uint8_t *pdata)
{
uint8_t command = pdata[2];
static uint8_t command21[] = {0x0D, 0x00, 0x21, 0x00, 0x02, 0x44, 0x09, 0x03, 0x4E, 0x00, 0x1E, 0x0A, 0xF2, 0x16};
static uint8_t command24[] = {0x05, 0x00, 0x24, 0x00, 0x25, 0x29};
static uint8_t command2F[] = {0x05, 0x00, 0x2F, 0x00, 0x8D, 0xCD};
static uint8_t command22[] = {0x05, 0x00, 0x22, 0x00, 0xF5, 0x7D};
static uint8_t command28[] = {0x05, 0x00, 0x28, 0x00, 0x85, 0x80};
static uint8_t command25[] = {0x05, 0x00, 0x25, 0x00, 0xFD, 0x30};
switch (command)
{
case 0x21:
Serial.write(command21, sizeof(command21));
break;
case 0x24:
Serial.write(command24, sizeof(command24));
break;
case 0x2F:
Serial.write(command2F, sizeof(command2F));
break;
case 0x22:
Serial.write(command22, sizeof(command22));
break;
case 0x28:
Serial.write(command28, sizeof(command28));
break;
case 0x25:
Serial.write(command25, sizeof(command25));
break;
}
}
unsigned int uiCrc16Cal(unsigned char const *pucY, unsigned char ucX)
{
const uint16_t PRESET_VALUE = 0xFFFF;
const uint16_t POLYNOMIAL = 0x8408;
unsigned char ucI, ucJ;
unsigned short int uiCrcValue = PRESET_VALUE;
for (ucI = 0; ucI < ucX; ucI++)
{
uiCrcValue = uiCrcValue ^ *(pucY + ucI);
for (ucJ = 0; ucJ < 8; ucJ++)
{
if (uiCrcValue & 0x0001)
{
uiCrcValue = (uiCrcValue >> 1) ^ POLYNOMIAL;
}
else
{
uiCrcValue = (uiCrcValue >> 1);
}
}
}
return uiCrcValue;
}
|
Código de Unity¶
El código de Unity está compuesto de tres clases. La primera clase implementa el protocolo, la segunda el controlador y a tercera es la lógica de la aplicación como tal.
Código que implementa el protocolo, SerialThreadRFIDProtocol.cs:
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 | using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO.Ports;
using System.Text;
public class SerialThreadRFIDProtocol : AbstractSerialThread
{
// Buffer where a single message must fit
private byte[] buffer = new byte[1024];
private int bufferUsed = 0;
private const int PRESET_VALUE = 0x0000FFFF;
private const int POLYNOMIAL = 0x00008408;
public SerialThreadRFIDProtocol(string portName,
int baudRate,
int delayBeforeReconnecting,
int maxUnreadMessages)
: base(portName, baudRate, delayBeforeReconnecting, maxUnreadMessages, false)
{
}
protected override void SendToWire(object message, SerialPort serialPort)
{
byte[] binaryMessage = (byte[])message;
serialPort.Write(binaryMessage, 0, binaryMessage.Length);
}
protected override object ReadFromWire(SerialPort serialPort)
{
if(serialPort.BytesToRead > 0)
{
serialPort.Read(buffer, 0, 1);
bufferUsed = 1;
// wait for the rest of data
while ( bufferUsed < (buffer[0] + 1) )
{
bufferUsed = bufferUsed + serialPort.Read(buffer, bufferUsed, buffer[0]);
}
// Verify Checksum and
if(verifyChecksum(buffer) == true)
{
// send the package to the application
byte[] returnBuffer = new byte[bufferUsed];
System.Array.Copy(buffer, returnBuffer, bufferUsed);
bufferUsed = 0;
return returnBuffer;
}
else
{
StringBuilder sb = new StringBuilder();
sb.Append("Packet: ");
foreach (byte data in buffer)
{
sb.Append(data.ToString("X2") + " ");
}
sb.Append("Checksum fails");
Debug.Log(sb);
return null;
}
}
else
{
return null;
}
}
private bool verifyChecksum(byte[] packet)
{
bool checksumOK = false;
byte ucI, ucJ;
int uiCrcValue = PRESET_VALUE;
int len = packet[0] + 1;
for (ucI = 0; ucI < (len - 2); ucI++)
{
uiCrcValue = uiCrcValue ^ packet[ucI];
for (ucJ = 0; ucJ < 8; ucJ++)
{
if ( (uiCrcValue & 0x00000001) == 0x00000001)
{
uiCrcValue = (uiCrcValue >> 1) ^ POLYNOMIAL;
}
else
{
uiCrcValue = (uiCrcValue >> 1);
}
}
}
if ((packet[len - 2] == LSBCkecksum) && (packet[len - 1] == MSBCkecksum)) checksumOK = true;
return checksumOK;
}
}
|
Código del controlador, SerialControllerRFIDProtocol.cs:
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 | using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
public class SerialControllerRFIDProtocol : MonoBehaviour
{
[Tooltip("Port name with which the SerialPort object will be created.")]
public string portName = "/dev/ttyUSB0";
[Tooltip("Baud rate that the serial device is using to transmit data.")]
public int baudRate = 57600;
[Tooltip("Reference to an scene object that will receive the events of connection, " +
"disconnection and the messages from the serial device.")]
public GameObject messageListener;
[Tooltip("After an error in the serial communication, or an unsuccessful " +
"connect, how many milliseconds we should wait.")]
public int reconnectionDelay = 1000;
[Tooltip("Maximum number of unread data messages in the queue. " +
"New messages will be discarded.")]
public int maxUnreadMessages = 1;
[Tooltip("Maximum number of unread data messages in the queue. " +
"New messages will be discarded.")]
// Internal reference to the Thread and the object that runs in it.
protected Thread thread;
protected SerialThreadRFIDProtocol serialThread;
// ------------------------------------------------------------------------
// Invoked whenever the SerialController gameobject is activated.
// It creates a new thread that tries to connect to the serial device
// and start reading from it.
// ------------------------------------------------------------------------
void OnEnable()
{
serialThread = new SerialThreadRFIDProtocol(portName,
baudRate,
reconnectionDelay,
maxUnreadMessages);
thread = new Thread(new ThreadStart(serialThread.RunForever));
thread.Start();
}
// ------------------------------------------------------------------------
// Invoked whenever the SerialController gameobject is deactivated.
// It stops and destroys the thread that was reading from the serial device.
// ------------------------------------------------------------------------
void OnDisable()
{
// If there is a user-defined tear-down function, execute it before
// closing the underlying COM port.
if (userDefinedTearDownFunction != null)
userDefinedTearDownFunction();
// The serialThread reference should never be null at this point,
// unless an Exception happened in the OnEnable(), in which case I've
// no idea what face Unity will make.
if (serialThread != null)
{
serialThread.RequestStop();
serialThread = null;
}
// This reference shouldn't be null at this point anyway.
if (thread != null)
{
thread.Join();
thread = null;
}
}
// ------------------------------------------------------------------------
// Polls messages from the queue that the SerialThread object keeps. Once a
// message has been polled it is removed from the queue. There are some
// special messages that mark the start/end of the communication with the
// device.
// ------------------------------------------------------------------------
void Update()
{
// If the user prefers to poll the messages instead of receiving them
// via SendMessage, then the message listener should be null.
if (messageListener == null)
return;
// Read the next message from the queue
byte[] message = ReadSerialMessage();
if (message == null)
return;
// Check if the message is plain data or a connect/disconnect event.
messageListener.SendMessage("OnMessageArrived", message);
}
// ------------------------------------------------------------------------
// Returns a new unread message from the serial device. You only need to
// call this if you don't provide a message listener.
// ------------------------------------------------------------------------
public byte[] ReadSerialMessage()
{
// Read the next message from the queue
return (byte[]) serialThread.ReadMessage();
}
// ------------------------------------------------------------------------
// Puts a message in the outgoing queue. The thread object will send the
// message to the serial device when it considers it's appropriate.
// ------------------------------------------------------------------------
public void SendSerialMessage(byte[] message)
{
serialThread.SendMessage(message);
}
// ------------------------------------------------------------------------
// Executes a user-defined function before Unity closes the COM port, so
// the user can send some tear-down message to the hardware reliably.
// ------------------------------------------------------------------------
public delegate void TearDownFunction();
private TearDownFunction userDefinedTearDownFunction;
public void SetTearDownFunction(TearDownFunction userFunction)
{
this.userDefinedTearDownFunction = userFunction;
}
}
|
Código de la aplicación como tal, SampleRFIDProtocol.cs:
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 | using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
public class SampleRFIDProtocol : MonoBehaviour
{
public SerialControllerRFIDProtocol serialController;
// Initialization
void Start()
{
serialController = GameObject.Find("SerialController").GetComponent<SerialControllerRFIDProtocol>();
Debug.Log("Q: 0x21, W: 0x24, E: 0x2F, R: 0x22, T: 0x28, Y: 0x25");
}
// Executed each frame
void Update()
{
//---------------------------------------------------------------------
// Send data
//---------------------------------------------------------------------
if (Input.GetKeyUp(KeyCode.Q))
{
Debug.Log("Command 04 FF 21 19 95 ");
serialController.SendSerialMessage(new byte[] { 0x04, 0xFF, 0x21, 0x19, 0x95 });
}
if (Input.GetKeyUp(KeyCode.W))
{
Debug.Log("Command 05 00 24 00 25 29");
serialController.SendSerialMessage(new byte[] { 0x05, 0x00, 0x24, 0x00, 0x25, 0x29 });
}
if (Input.GetKeyUp(KeyCode.E))
{
Debug.Log("Command 05 00 2F 1E 72 34 ");
serialController.SendSerialMessage(new byte[] { 0x05, 0x00, 0x2F, 0x1E, 0x72, 0x34 });
}
if (Input.GetKeyUp(KeyCode.R))
{
Debug.Log("Command 06 00 22 31 80 E1 96 ");
serialController.SendSerialMessage(new byte[] { 0x06, 0x00, 0x22, 0x31, 0x80, 0xE1, 0x96 });
}
if (Input.GetKeyUp(KeyCode.T))
{
Debug.Log("Command 05 00 28 05 28 D7 ");
serialController.SendSerialMessage(new byte[] { 0x05, 0x00, 0x28, 0x05, 0x28, 0xD7 });
}
if (Input.GetKeyUp(KeyCode.Y))
{
Debug.Log("Command 05 00 25 0A A7 9F ");
serialController.SendSerialMessage(new byte[] { 0x05, 0x00, 0x25, 0x00, 0xFD, 0x30 });
}
//---------------------------------------------------------------------
// Receive data
//---------------------------------------------------------------------
byte[] message = serialController.ReadSerialMessage();
if (message == null)
return;
StringBuilder sb = new StringBuilder();
sb.Append("Packet: ");
foreach (byte data in message)
{
sb.Append(data.ToString("X2") + " ");
}
Debug.Log(sb);
}
}
|
Evaluación sumativa 1¶
En este enlace se puede descargar el enunciado de la evaluación sumativa 1.