App: Wixel in little robot powered by step motors

This is my first Wixel application, actually also my first experience with a microcontroller, so I had some difficulty to learn enough to make this program
and probably this is not the best code, but that’s what I get at this time and apparently is working well.

I had a lot of help from this forum and want to thank David for his patience and fast and accurate answers. I’m putting this code to help the beginners and
perhaps also to receive suggestions from Wixel’s masters circulating here.
Would be very welcome.

The application is relatively simple and involves a small robot powered by unipolar stepper motors taken from scrapped printers.

Computer calculates the setting of bits to enable pins connected to stepper motors and also calculates time for each pulse should last. For each pulse, two bytes are sent to Wixel, one for pin and one for pulse duration. Even robot moves few inches, may be needed hundreds of pulses. So, application implements a circular buffer capable to store 1024 pulses and makes necessary controls to send pulses to pins in correct sequence and duration.

According pulses will run, will be released in buffer space, so, computer can send more pulses, even as part of previous is still running.

In my case, duration of pulses ranges from 12ms (cruising speed) to 40ms (starting speed) so 1024 pulses means at least 12 seconds of robot’s motion. That’s enough time to computer prepare and send following pulses with enough slack.

The Wixel connected to computer has no processing other than relay messages exchanged between robot and computer. In future versions I’m considering changing it, might be used as second buffer or as auxiliary processor.

Below the source code. The program provides a simple messaging protocol between two ends. Messages are sent in packets with a head byte and a variable number of bytes to payload. The head byte uses 2 bits to inform message size and other 6 to message. So we can have 64 different messages and choose from 4 standard sizes.

Much of communication is done with messages that require no parameters, so message can have only head byte without payload.

main loop: a1Robot.c

/*
 * a1Robot.c
 *
 *  Created on: 23/10/2012
 *      Author: Francesco A. Perrotti
 */

#include <wixel.h>
#include <usb.h>
#include <usb_com.h>
#include <radio_com.h>
#include <gpio.h>
#include "rbTypes.h"
#include "rbLeds.h"
#include "rbMessages.h"
#include "rbPacks.h"
#include "rbPinsCtrl.h"

TMsgPacket rxCurr;

uint16 XDATA rxCount = 0;
uint16 XDATA txCount = 0;
uint16 XDATA timeToTalk = 15000;

BIT talkOn = true;
uint32 lastTalk = 0;

// Send a packet to computer
void sendMsgPack(TMsgPacket *mp)
{
	uint8 bytesLeft = msgPayloadSize(mp->head)+1;
	TByte *pt;

	// wait to get available
	while(radioComTxAvailable () < bytesLeft)
        radioComTxService();

	// now we can send
	pt = (TByte*)mp;
	while(bytesLeft)
	{
		radioComTxSendByte(*pt);
		pt++;
		bytesLeft--;
		txCount++;
	}
	// a message was sent
	yellowOn = 0x3000;
}

// All received packets are processed immediately
//
void onRxPack()
{
	uint8 msg, msize;
	TMsgPacket mpSend;
	msg = msgNumber(rxCurr.head);
	msize = msgPayloadSize(rxCurr.head);

	// messages process
	switch (msg) {

		case msg_ACK_REQUEST:
			sendMsgPack
			(
				makePack_0(&mpSend, msg_ACK)
			);
		break;

		case msg_SERIAL_REQUEST:
		    sendMsgPack
		    (
		    	makePack_4L(&mpSend, msg_SERIAL_REPORT, *((int32*)(serialNumber)))
		    );
		break;

		// turn off imHere messages
		case msg_STOP_TALKING:
			talkOn = false;
		break;

		// turn on imHere messages
		case msg_TALK_WITH_ME:
			talkOn = true;
		break;

		// some info
		case msg_INFO_REQUEST:
			sendMsgPack
			(
				makePack_8wL(&mpSend, msg_INFO_REPORT,
						rxCount, txCount, *((int32*)(PROTOCOL_VERSION)))
			);
		break;

		// send byte directly to pins
		case msg_BYTE_TO_PINS:
			P1 = rxCurr.pay.byte1;
		break;

		// send free buffer space
		case msg_FREE_SPACE_REQUEST:
			sendMsgPack
			(
			    makePack_2w(&mpSend, msg_FREE_SPACE_REPORT, pulsesFree())
			);
		break;

		// add pulse to buffer
		case msg_PULSE_TO_BUFF:
			if(!addPulseToBuff(&rxCurr))
				sendMsgPack
				(
					makePack_0(&mpSend, err_BUFFER_FULL)
				);
		break;

		// execute pulses
		case msg_START_PULSES:
			pulseOn();
		break;

		// stop pulse execution
		case msg_STOP_PULSES:
			pulseOff();
		break;

		// Unknown message was received
		// Send wrong message back after error message
		default:
			sendMsgPack
			(
				makePack_0(&mpSend, err_UNKNOWN_MSG)
			);

			mpSend.head = rxCurr.head;
			mpSend.pay.long1 = rxCurr.pay.long1;
			mpSend.pay.long2 = rxCurr.pay.long2;
			sendMsgPack(&mpSend);
		break;
	}
}

void radioRxService()
{	uint8 addResult;

	while(radioComRxAvailable())
	{
		addResult= addByteToPack(&rxCurr, radioComRxReceiveByte());
		rxCount++;
		if (addResult == bp_PACKET_CLOSED)
			onRxPack(); // um pacote acaba de ser completado
	}
}

void imHere()
{
	if(!talkOn) return;
	if( ((getMs()-lastTalk)>timeToTalk) 	&&
		(radioComTxAvailable()>=sizeof(TMsgPacket))
	  )
	{
		TMsgPacket mp;
		mp.head = (msg_IM_HERE | mask_8Bytes);
		mp.pay.word1 = rxCount;
		mp.pay.word2 = txCount;
		mp.pay.word3 = pulsesFree();
		sendMsgPack(&mp);
		lastTalk = getMs();
	}
}

void main()
{
    systemInit();
    usbInit();
    radioComInit();

    // port 1 to digital output
    setDigitalOutput(10, LOW);
    setDigitalOutput(11, LOW);
    setDigitalOutput(12, LOW);
    setDigitalOutput(13, LOW);
    setDigitalOutput(14, LOW);
    setDigitalOutput(15, LOW);
    setDigitalOutput(16, LOW);
    setDigitalOutput(17, LOW);

    // Reset value
    P1= 0b10000001;

    // blink
    yellowOn = 0x6000;
    redOn = 0x3000;

    while(1)
    {
        updateLeds();
        boardService();
        usbComService();

        radioRxService();
        pulseCheck();
        imHere();
        radioComTxService();
    }
}

some common defs: rbTypes.h

/*
 * rbTypes.h
 *
 *  Created on: 23/10/2012
 *      Author: Francesco A. Perrotti
 */

#ifndef RBTYPES_H_
#define RBTYPES_H_

#include <wixel.h>

#define BOOL uint8
#define false 0
#define true  1

#define mask_2Bytes (1 << 6)
#define mask_4Bytes (2 << 6)
#define mask_8Bytes (3 << 6)

typedef uint8 XDATA TByte;

#endif /* RBTYPES_H_ */

Small led control. Implements updateLeds function

/*
 * rbLeds.h
 *
 *  Created on: 23/10/2012
 *      Author: Francesco A. Perrotti
 */

#ifndef RBLEDS_H_
#define RBLEDS_H_

#include <wixel.h>

extern uint16 XDATA redOn;
extern uint16 XDATA yellowOn;

void updateLeds();


#endif /* RBLEDS_H_ */
/*
 * rbLeds.c
 *
 *  Created on: 23/10/2012
 *      Author: Francesco A. Perrotti
 */
#include "rbLeds.h"

#include <usb.h>
#include "rbLeds.h"

uint16 XDATA redOn = 0;
uint16 XDATA yellowOn = 0;

void updateLeds()
{
    usbShowStatusWithGreenLed();
	LED_RED(redOn);			if(redOn) redOn--;
	LED_YELLOW(yellowOn);	if(yellowOn) yellowOn--;
}

Message protocol

/*
 * rbMessages.h
 *
 *  Created on: 23/10/2012
 *      Author: Francesco A. Perrotti
 */

#ifndef RBMESSAGES_H_
#define RBMESSAGES_H_

#include <wixel.h>
#include "rbTypes.h"

// struct {
//    uint8  head;    // a dita cuja mensagem e o tamanho do payload
//    TPayLoad pay; // todo o resto que vem depois
// }
//
// 0bSSMMMMMM: bits SS para o tamanho e MMMMMM para a mensagem (64 msgs)
// SS: 00- 0 bytes 
//     01- 2 bytes
//     10- 4 bytes
//     11- 8 bytes
//

// Payload 
typedef union
{
	struct{
		uint8
			byte1,
			byte2,
			byte3,
			byte4;
	};
	struct{
		uint16
			word1,
			word2,
   			word3,
    		word4;
	};
	struct{
		int32
			long1,
		    long2;
	};
	uint8 vt[8];
}TPayLoad;

// Msg packet
typedef struct
{
    uint8 head;
	TPayLoad pay;

}_MsgPacket;

typedef _MsgPacket XDATA TMsgPacket;

uint8 msgPayloadSize(uint8 msg);
#define msgNumber(msg) (msg & 0b00111111)

////////////////////////////////////////////////////////////////////////
///
///		Protocolo de comunicação do robo
///
////////////////////////////////////////////////////////////////////////

extern char PROTOCOL_VERSION[];

// Ack: equivalente a um OK
#define msg_ACK  42

// Pedido de ack. O wixel deve enviar enviar um ack de volta
#define msg_ACK_REQUEST 1

// Pedido de numero serial.
#define msg_SERIAL_REQUEST 2

// Informa o numero serial.
#define msg_SERIAL_REPORT 3

// Usada para o wixel informar ao computador que ainda está vivo.
#define msg_IM_HERE 4

// Pede o espaço disponivel no buffer
#define msg_FREE_SPACE_REQUEST  5

// Informa o espaço disponivel no buffer
#define msg_FREE_SPACE_REPORT  6

// Desativa msgs imHere
#define msg_STOP_TALKING   7

// Ativa msgs imHere
#define msg_TALK_WITH_ME   8

// Pede informação do wixel
#define msg_INFO_REQUEST   9

// Reporta informação do wixel
#define msg_INFO_REPORT   10

// manda um byte diretamente para os pinos de saída dos motores
#define msg_BYTE_TO_PINS   24

// manda um pulso (pinos+tempo) para o buffer de pulsos
#define msg_PULSE_TO_BUFF 25

// Inicia a execução dos pulsos armazenados no buffer
#define msg_START_PULSES  26

// Interrompe a execução dos pulsos
#define msg_STOP_PULSES  27


/// Erros
#define err_UNKNOWN_MSG   63
#define err_BUFFER_FULL   62

#endif /* RBMESSAGES_H_ */
/*
 * rbMessages.c
 *
 *  Created on: 23/10/2012
 *      Author: Francesco A. Perrotti
 */

#include "rbMessages.h"

char PROTOCOL_VERSION[] =  "001a";

uint8 msgPayloadSize(uint8 msg)
{
	switch ((msg & 0b11000000) >> 6) {
		case 1: return 2;
		case 2: return 4;
		case 3: return 8;
		default: return 0;
	}
}

Pack support

/*
 * rbPacks.h Monta os pacotes de msgs
 *
 *  Created on: 23/10/2012
 *      Author: Francesco A. Perrotti
 */

#ifndef RBPACKS_H_
#define RBPACKS_H_

#include "rbTypes.h"
#include "rbMessages.h"

// Eventos retornados por addByteToPack

// Novo pacote iniciado
#define bp_NEW_RX_PACKET 1
// Pacote em progresso
#define bp_RX_PACKET_ON 2
// Pacote encerrado sem erros
#define bp_PACKET_CLOSED 3

// Mount packets byte by byte and informs when
// packet is complet.
uint8 addByteToPack(TMsgPacket *msgPack, uint8 byte);

TMsgPacket* makePack_0(TMsgPacket *mp, uint8 msg);

TMsgPacket* makePack_2b(TMsgPacket *mp, uint8 msg,
		uint8 byte1, uint8 byte2);

TMsgPacket* makePack_2w(TMsgPacket *mp,
		uint8 msg, uint16 word1);

TMsgPacket* makePack_4L(TMsgPacket *mp, uint8 msg,
		int32 long1);

TMsgPacket* makePack_8wL(TMsgPacket *mp, uint8 msg,
		uint16 word1,
        uint16 word2,
        int32 long2);

#endif /* RBPACKS_H_ */
/*
 * rbPacks.c Monta os pacotes de msgs byte a byte.
 *
 *  Created on: 23/10/2012
 *      Author: Francesco A. Perrotti
 */

#include "rbPacks.h"

BIT rxOn = false;
uint8 bytesLeft = 0;
uint8 btRcv;
TByte *Current;


int8 closePack()
{
	bytesLeft = 0;
	btRcv = 0;
	rxOn= false;
	return bp_PACKET_CLOSED;
}

uint8 addByteToPack(TMsgPacket *msgPack, uint8 byte)
{
	if(!rxOn)               // Iniciando novo pacote
	{
		rxOn= true;
		msgPack->head = byte;
		bytesLeft = msgPayloadSize(byte);
		btRcv=1;
		Current= (TByte*)msgPack;

		if(!bytesLeft)  // só tem um byte na msg
		{
			return closePack();
		}
		else return bp_NEW_RX_PACKET;
	}
	else           // Pacote em andamento
	{
		Current++;
		*Current = byte;
		if (bytesLeft > 1)
		{
			bytesLeft--;
			btRcv++;
			return bp_RX_PACKET_ON;
		}
	    else  // Ultimo byte do pacote
	    	return closePack();
	}
}

TMsgPacket* makePack_0(TMsgPacket *mp, uint8 msg)
{
	mp->head = (msg & 0b00111111);
	return mp;
}

TMsgPacket* makePack_2b(TMsgPacket *mp, uint8 msg, uint8 byte1, uint8 byte2)
{
	mp->head = (msg & 0b00111111) | mask_2Bytes;
	mp->pay.byte1= byte1;
	mp->pay.byte2= byte2;
	return mp;
}

TMsgPacket* makePack_2w(TMsgPacket *mp, uint8 msg, uint16 word1)
{
	mp->head = (msg & 0b00111111) | mask_2Bytes;
	mp->pay.word1= word1;
	return mp;
}

TMsgPacket* makePack_4L(TMsgPacket *mp, uint8 msg, int32 long1)
{
	mp->head = ((msg & 0b00111111) | mask_4Bytes);
	mp->pay.long1 = long1;
	return mp;
}

//TMsgPacket* makePack_8bwL(TMsgPacket *mp, uint8 msg,
//		uint8 byte1, uint8 byte2,
//        uint16 word2,
//        int32 long2)
//{
//	mp->head = (msg & 0b00111111) | mask_8Bytes;
//	mp->pay.byte1= byte1;
//	mp->pay.byte2= byte2;
//	mp->pay.word2= word2;
//	mp->pay.long2= long2;
//	return mp;
//}

TMsgPacket* makePack_8wL(TMsgPacket *mp,
		uint8 msg,
		uint16 word1,
        uint16 word2,
        int32 long2)
{
	mp->head = (msg & 0b00111111) | mask_8Bytes;
	mp->pay.word1= word1;
	mp->pay.word2= word2;
	mp->pay.long2= long2;
	return mp;
}

Pulse buffer and exec. pulseCheck() needs to be called frequently

/*
 * rbPinsCtrl.h
 *
 *  Created on: 23/10/2012
 *      Author: Francesco A. Perrotti
 */

#ifndef RBPINSCTRL_H_
#define RBPINSCTRL_H_

#include <wixel.h>
#include "rbTypes.h"
#include "rbMessages.h"

BOOL addPulseToBuff(TMsgPacket *mp);
void pulseCheck();
void pulseOn();
void pulseOff();
uint16 pulsesCount();
uint16 pulsesFree();

#endif /* RBPINSCTRL_H_ */
/*
 * rbPinsCtrl.c
 *
 *  Created on: 23/10/2012
 *      Author: Francesco A. Perrotti
 */

#include <cc2511_map.h>
#include <gpio.h>
#include "rbLeds.h"
#include "rbPinsCtrl.h"

typedef struct
{
	uint8 pins;
	uint8 time;

}_PinsPulse;

typedef _PinsPulse XDATA TPinsPulse;

#define BUF_LEN 1024

// circular increment
#define incC(value) (value= (value+1) % BUF_LEN)

TPinsPulse pulseBuf [BUF_LEN];
uint16 first=0;
uint16 next=0;
uint16 freeBuffs = BUF_LEN;
uint16 bufCount = 0;

uint32 nextPulse = 0;
BIT execModeOn = false;

BOOL addPulseToBuff(TMsgPacket *mp)
{
	if(!freeBuffs) return false;

	pulseBuf[next].pins = mp->pay.byte1;
	pulseBuf[next].time = mp->pay.byte2;
	freeBuffs--;
	bufCount++;
	incC(next);
	return true;
}

uint16 pulsesCount()
{
	return bufCount;
}

uint16 pulsesFree()
{
	return freeBuffs;
}

TPinsPulse *getFirst()
{
	if(!bufCount) return 0;
	return &pulseBuf[first];
}

BOOL moveOn()
{
	if(freeBuffs==BUF_LEN) return false;
	incC(first);
	freeBuffs++;
	bufCount--;
	return true;
}

void executeNextPulse()
{   TPinsPulse *pp = getFirst();
    if(!pp) return;

	P1 = pp->pins;
	nextPulse = getMs() + pp->time;
	moveOn();
}

void pulseOn()
{
	execModeOn = true;
	nextPulse = 0;
}

void pulseOff()
{
	execModeOn = false;
}

void pulseCheck()
{
	if(!execModeOn) return;
	if(!bufCount) return;
	if(getMs()<nextPulse) return;

	executeNextPulse();
}

Hello!

I am glad that you have had success with the Wixel, and thank you for posting information about your project here! I was having trouble reliably loading your image from blogspot.com, so I copied it and uploaded it as an attachment to forum:


–David

Excellent job, congratulations!!!