Ce projet a pour but de faciliter la création d'applications permettant de contrôler une carte arduino depuis un PC, via le cable usb. Pour cela, ce projet comporte trois parties :
Un programme tourne en boucle sur la carte Arduino. Ce programme fait en permanence 3 choses :
Coté PC, ce projet est fait en C# et a pour but de créer des composants .net permettant de faire facilement des applications utilisant l'arduino. Le composant fondamental du projet est ArduinoCtrl. Ce composant permet de faire le lien entre l'application et la carte. Plusieurs controles permettent d'accèder aux ressources de la carte : Ex, une trackbar permet de contrôler l'état de sortie d'une sortie PWM.
Enfin en ce qui concerne le protocole d'échange entre les deux parties, il s'agit d'un protocole simple sur le principe suivant : un message "O/P/V" indique un ordre 'O', concerne la pin 'P', avec la valeur 'V'. Un exemple : P/12/1250 indique à la pin '12' d'effectuer un pulse 'P' de longueur '1250'ms.
Actuellement, le protocole suivant est implémenté :
'D' : Met la DO (digital output) à la valeur passée en paramètre. Dans le cas d'une DO simple, les valeurs supportée sont 0 (état LOW) et 255 (état HIGH). Dans le cas d'un DO PWM, toutes les valeurs entre 0 et 255 sont valables.
'S' (Set) : Indique le changer le mode d'une DIO.
'M' (Mesure) : Indique si l'AI concernée doit être scrutée ou non.
'R' (Rate) : Indique un temps minimum (en ms) en deça duquel un changement de valeur de l'AI sera ignoré.
'P' (Pulse) : Indique à la carte de générer un pulse (passage en état HIGH puis en LOW), d'une longueur passée en paramètre. Cette commande est trés utile pour le controle de servos-moteurs.
'D' : indique un changement d'état de la DI concernée.
'A' : indique un changement de valeur de l'AI concernée.
Voici le code embarqué :
/*
* UsbControl
* by Pascal BUIREY
*
* embedded software to communicate with monitoring software
* messages are in the following format :
* arduino => PC
* 1/N/V A/N/V Send the Analog value V read from pin N
* 2/N/V D/N/V Send the Digital value V read from pin N
* 64/N/V Send return value V for initialisation on device N
* 65/N/V Send the byte V read on SPI Device #N
*
* PC => arduino
* 0/N/V S/N/V Set the pinmode for the specified pin 0=ignore, 1=INPUT, 2=OUTPUT
* 1/N/V M/N/V set the Measure pin for INPUT (1) or ignore(0);
* 2/N/V D/N/V write the value V on digital pin N 0=LOW, 255=HIGH.
* 99/N/V T/N/V activate/deactivate traces
* intermediate values are possible for PWM digital IOs.
*
* SPI commands :
* 6/N/V : Declare SPI device N is ignored V=SPI Id (0-3);
* 60/N/V :setup SPI Device #N Chip Select pin = V;
* 61/N/V :setup SPI Device #N Clock pin = V;
* 62/N/V :setup SPI Device #N MISO pin = V;
* 63/N/V :setup SPI Device #N MOSI pin = V;
* 64/N/V :initialize SPI Device (pins must be setup)
* 65/N/V :on SPI Device #N, write the byte V
*
*/
//SPI opcodes
#define WREN 6
#define WRDI 4
#define RDSR 5
#define WRSR 1
#define READ 3
#define WRITE 2
class SPIDevice {
private :
byte clr;
public:
short MOSIPin; //Master Out Slave In
short MISOPin; //Master In Slave Out
short ClockPin; //Clock
short ChipSelectPin; //CS Chip Select
SPIDevice() {
MOSIPin = -1;
MISOPin = -1;
ClockPin = -1;
ChipSelectPin = -1;
}
byte SPI_Init() {
short ret_val=0;
//verify that pins have been setup
if ((MOSIPin==-1)||
(MISOPin == -1)||
(ClockPin == -1)||
(ChipSelectPin == -1)) {
return 0;
}
//Setup SPI Pins
pinMode(MOSIPin, OUTPUT);
pinMode(MISOPin, INPUT);
pinMode(ClockPin,OUTPUT);
pinMode(ChipSelectPin,OUTPUT);
digitalWrite(ChipSelectPin,HIGH); //disable device
//Setup SPI Protocol
// SPCR = 01010000
//interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
//sample on leading edge of clk,system clock/4 rate (fastest)
SPCR = (1<<SPE)|(1<<MSTR);
clr=SPSR;
clr=SPDR;
delay(10);
return(1);
}
// transfer Data to/from SPI Device
char spi_transfert(volatile char data)
{
SPDR = data; // Start the transmission
while (!(SPSR & (1<<SPIF))) // Wait for the end of the transmission
{
};
return SPDR; // return the received byte
}
char spi_write(char c) {
spi_transfert(c);
}
char spi_read() {
return spi_transfert(0XFF);
}
~SPIDevice() {
}
};
/*pin modes stored as an array of bytes 0=ignore 1=input, 2=output*/
byte diPinMode[14];
byte aiPinMode[6];
int dioPinValues[14];
int aiPinValues[6];
int aiRefreshRate[6];
unsigned long aiLastRefresh[6];
//support up to 4 SPI devices
SPIDevice *SPIDevices[4];
/*incoming message from monitoring*/
/*maximum message size*/
char sIncomingMsg[255];
/*current index of received chars*/
int iCurrentMsgIdx;
boolean bMsgComplete;
boolean bTraceOn;
void Trace(char* text) {
if (bTraceOn) {
Serial.print("TRACE : ");
Serial.println(text);
}
}
/*sendToMonitoring*/
/*send data for monitoring software*/
/*pinType = 'D' for digitals or 'A' for analogs*/
void sendToMonitoring(char pinType,byte pinNumber,unsigned int value) {
Serial.print(pinType);
Serial.print("/");
Serial.print(pinNumber,DEC);
Serial.print("/");
Serial.println(value,DEC);
}
void check4DIOChanges() {
/*pins 0 and 1 are used for serial communication*/
for (int i=2;i<14;i++) {
/*if this is not an output pin*/
if (diPinMode[i]==1) {
int newval = digitalRead(i);
/*if the value for that pin as changed*/
if (newval!=dioPinValues[i]) {
/*send the new value to monitoring software*/
sendToMonitoring('D',i,newval);
/*store the new value*/
dioPinValues[i]=newval;
}
}
}
}
void check4AIChanges() {
for (int i=0;i<6;i++) {
if (aiPinMode[i]==1) {
unsigned long curTime = millis();
if ((aiLastRefresh[i]+aiRefreshRate[i])<curTime) {
aiLastRefresh[i]=curTime;
int newval = analogRead(i);
/*if the value for that pin as changed*/
if (newval!=aiPinValues[i]) {
/*send the new value to monitoring software*/
sendToMonitoring('A',i,newval);
/*store the new value*/
aiPinValues[i]=newval;
}
}
}
}
}
/*parse the incoming message and perform corresponding action*/
void parseIncomingMsg(char *msg) {
int pinNumber = 0;
int value=0;
int idx=0;
/*get the pin number*/
/* for debug*/
Trace("Parsing ");
Trace(msg);
//read the Command...
while (msg[idx]!='/') {
idx++;
}
idx++;
while (msg[idx]!='/') {
pinNumber=pinNumber*10+msg[idx]-'0';
idx++;
}
idx++;
//for debug only
/*get the value*/
while (msg[idx]!='\0') {
if ((msg[idx]>='0')&&(msg[idx]<='9')) {
value=value*10+msg[idx]-'0';
}
idx++;
}
switch(msg[0]) {
/*incoming order to output on a DIO */
case '?' : {
Serial.println("Arduino");
}
case 'D' : {
if((value == 255)||(value==0)) {
if (value==255) {
digitalWrite(pinNumber,HIGH);
}
if (value==0) {
digitalWrite(pinNumber,LOW);
}
}else{
analogWrite(pinNumber,value);
}
};break;
/*incoming ordre for changing pinmode*/
case 'S' : {
/*ignored pin*/
if (value==0) {
diPinMode[pinNumber]=0;
}
/*set pin for reading*/
if (value==1) {
pinMode(pinNumber,INPUT);
diPinMode[pinNumber]=1;
}
/*set pin for writing*/
if (value==2) {
pinMode(pinNumber,OUTPUT);
diPinMode[pinNumber]=2;
Trace("Pin set");
}
};break;
case 'M' : {
if (value ==0) {
aiPinMode[pinNumber]=0;
}
if (value == 1) {
aiPinMode[pinNumber]=1;
}
};break;
case 'R' : {
aiRefreshRate[pinNumber]=value;
}
case 'P' : { /*P = Pulse value = pulse length (milliseconds)*/
if (value !=0) {
if (diPinMode[pinNumber]==2) {
digitalWrite(pinNumber,HIGH);
delayMicroseconds(value);
digitalWrite(pinNumber,LOW);
}
}
};break;
case 'T' : { /*T = activate(1)/deactivate(0) traces*/
bTraceOn=(value==1);
Trace("Trace mode set");
};break;
/*default : wrong message*/
default : {
//for debug only
Trace("Error parsing message : ");
Trace(msg);
};break;
}
Trace("Processed ok");
}
void check4IncomingMsg() {
/*if com is Ok ?*/
int nbBytes =Serial.available();
if (nbBytes>0) {
char msg[nbBytes+1];
/*read incoming bytes*/
char c='\0';
int i=0;
for (i=0;(i<nbBytes)&&(c!=10);i++) {
c = Serial.read();
msg[i]=c;
}
/*10 indicates the end of a message*/
if (c==10) {
//msg[i]='\0';
bMsgComplete=true;
}
/*add read bytes to current message*/
for (int j=0;j<i;j++) {
sIncomingMsg[iCurrentMsgIdx] = msg[j];
iCurrentMsgIdx++;
}
if (bMsgComplete) {
sIncomingMsg[iCurrentMsgIdx]='\0';
// for debug only
Trace("Received");
Trace(sIncomingMsg);
parseIncomingMsg(sIncomingMsg);
/*message treated, reset the buffer*/
bMsgComplete=false;
iCurrentMsgIdx=0;
}
}
}
void setup()
{
// begin the serial communication
Serial.begin(19200);
bMsgComplete=false;
iCurrentMsgIdx=0;
/*Init all pins to INPUT, and their values to 0*/
/*pins 0 and 1 are used in serial communication*/
for (int i=2;i<14;i++) {
diPinMode[i]=0;
dioPinValues[i]=0;
pinMode(i,INPUT);
}
/*init ai pins values*/
for (int i=0;i<6;i++) {
aiPinMode[i]=0;
aiPinValues[i]=0;
/*refresh every second by default*/
aiRefreshRate[i]=1000;
aiLastRefresh[i]=millis();
}
}
void loop()
{
/*check for changes on AI and DIO. If changes happen, send them to monitoring*/
check4DIOChanges();
check4AIChanges();
/*check for message coming from monitoring*/
//delay(100);
check4IncomingMsg();
}
Il y a surement pas mal à redire sur ce code, et des optimisations à faire, mais j'ai voulu privilegier la facilité de lecture.
Pascal BUIREY.