Microcontroller Projects

Robots are cool.

Below are some C-based projects I put together for my DFRobot Romeo AOI Controller. It's essentailly an Arduino UNO with a motor driver, and can run the same source code.

This is mostly preparation for my planned wine-serving robot.

Binary LED Counter

This program translates a binary value into a sequence of illuminated LEDs.
Can be used as a very niche 8-bit record-keeping device (for nerds only).

/*
Binary LED Counter
James Yoannou, 2019

Using momentary switches with state machines to increment and decrement a binary value, displayed by LEDs.

--------------------------------------------------------------
CONNECTION DIAGRAM:

Atmega328p:		Romeo Board:	I/O Trainer:	Component:

LEDS:
PD2 ->			D2 ->			JP3_1 ->		D1A
PD3 ->			D3 ->			JP3_2 ->		D1B
PD4 ->			D4 ->			JP3_3 ->		D1C
PD5 ->			D5 ->			JP3_4 ->		D1D
PD6 ->			D6 ->			JP3_5 ->		D1E
PD7 ->			D7 ->			JP3_6 ->		D1F
PB0 ->			D8 ->			JP3_7 ->		D1G
PB1 ->			D9 ->			JP3_8 ->		D1H

Switches:
PB3 ->			D11	->			JP2_6 ->		S2
PB4 ->			D12	->			JP2_5 ->		S1
--------------------------------------------------------------

*/

#include <util/delay.h>
#include <avr/io.h>
#define F_CPU 16000000UL

// Returns true if value of bit is 1:
#define BIT_IS_SET(byte, bit) (byte & (1 << bit))
// Returns true if value of bit is 0:
#define BIT_IS_CLEAR(byte, bit) (!(byte & (1 << bit)))

void pause_ms(uint16_t ms);
int btt1 = 0;
int btt2 = 0;
// Count could also be a uint8_t (8-bit) to save space, and will automatically overflow:
int count = 0;

int main(void)
{
	// Ports
	DDRD = 0b11111100;
	DDRB = 0b00000011;
	PORTD = 0b00000000;
	PORTB = 0b00011000;

	while (1)
	{
		/*Set each LED to change according to count*/
		PORTD = count << 2;

		// Bit-twiddling is necessary for PORTB to avoid changing the whole port:
		if (BIT_IS_SET(count, 6))
			PORTB |= (1 << 0);
		else
			PORTB &= ~(1 << 0);
		if (BIT_IS_SET(count, 7))
			PORTB |= (1 << 1);
		else
			PORTB &= ~(1 << 1);


		/*State machines to instantiate button presses: */

		// STATE MACHINE S1:
		if (btt1 == 0) {
			// Check for press: value of 0 means the button is being pressed:
			if (BIT_IS_CLEAR(PINB, PB3)) {
				btt1 = 1; // We hold here, increment on release (1)!
			}
		}
		else {
			// Now we take action if the button is released:
			if(BIT_IS_SET(PINB, PB3)){
				btt1 = 0;
				if (count == 0)
					count = 255;
				else
					count--;
				// Quick pause to stop double-input:
			}
		}

		// STATE MACHINE S2:
		if (btt2 == 0) {
			if (BIT_IS_CLEAR(PINB, PB4)) {
				btt2 = 1;
			}
		}
		else {
			if (BIT_IS_SET(PINB, PB4)) {
				btt2 = 0;
				if (count == 255)
					count = 0;
				else
					count++;
			}
		}
		pause_ms(5);
	}
	return 0;
}

// For smooth button pressing
void pause_ms(uint16_t ms)
{
	uint16_t i;
	for (i = 0; i < ms; i++)
		_delay_ms(5);
}
                            
                        
                        

Theremin

Input values from a ranged sensor can be used in all sorts of creative ways.
As a musician, there was only one way for me: To make some noise.

/*
Theremin
James Yoannou, 2019

Creating a theremin out of a buzzer using the SHARP distance sensor.

When the switch is held, an interrupt is triggered and:
We convert the sensor's return voltage into the desired frequency for the buzzer PWM signal.

When the switch is not held, we exit the interrupt loop and:
Shut down the buzzer in the main method.

-------------------------------------------------------------
CONNECTION DIAGRAM:
Atmega328P:	Romeo Board:	I/O Trainer Board	Component	

PB1 ->		D9 ->			JP4_2 ->			BZ1
PD2 ->		D2 ->			JP2_5 ->			S1
PC5 ->		A5 ->			----- ->			SHARP Sensor
-------------------------------------------------------------

*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
#define F_CPU 16000000UL
#define BAUD 9600
#define PRESCALE 8
#define BIT_IS_SET(byte, bit) (byte & (1 << bit))
#define BIT_IS_CLEAR(byte, bit) (!(byte & (1 << bit)))

void initTimer(void);
void initInterrupts(void);

void initADC(void);
uint16_t analog(uint8_t channel);
uint16_t medFilter(uint16_t signal, uint16_t array[11]); // This caused delay issues
float map(float v, float a, float b, float c, float d); // Mike's mapping function

void initUART(unsigned int baud);
void transmitByte(unsigned char data);
void printDec(int num);
void transmitString(char* StringPtr);

// For my abandonned filter function (unused):
uint16_t medArray[11];
uint8_t count = 0;
uint16_t median;


uint16_t converter; // To convert the signal to a 0-4000 value.


int main(void)
{
	initInterrupts();
	initUART(BAUD);
	initTimer();
	initADC();

	DDRD = 0;
	PORTD = (1 << PD2);
	// Switch S1 will trigger interrupt when pressed

	while (1)
	{
		DDRB = 0;
		// Clear buzzer output (mute)
	}
	return 1;
}


void initTimer(void)
{
	TCCR1A = (1 << COM1A1) | (1 << WGM11);
	// Set timer/counter properties for fast PWM
	TIMSK1 = (1 << ICIE1);
	// Ensure ICR1 is used as TOP
	ICR1 = 0;
	OCR1A = (ICR1 / 2);
	// 50% duty cycle
	TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
	// CS11 sets clock prescalar to 8 and starts timer
}


void initInterrupts(void)
{
	EIMSK |= (1 << INT0);
	// Turns PD2 int INT0, enables interrupts on it
	EICRA |= (1 << ISC01);
	// Set INT0 to trigger when high -> low (switch press)
	sei();
	// Enables all external interrupts
}


// map number v from range a-b to range c-d
float map(float v, float a, float b, float c, float d)
{
	return (v - a) * (d - c) / (b - a) + c;
}


// Interrupt on switch press:
ISR(INT0_vect)
{
	// Run buzzer tone loop until switch is released:
	while (BIT_IS_CLEAR(PIND, PD2))
	{
		//median = medFilter(analog(5), medArray);
		// CAUSED DELAYS - NEED FASTER FILTER. Will use raw analog(5) value for now

		DDRB |= (1 << PB1);
		PORTB |= (1 << PB1);
		// Set buzzer output
		converter = 4000 - (map((float)analog(5), 0.0, 620.0, 0.0, 4000.0));
		// Map raw signal value to 0-4000 range, invert it (for theremin direction)
		ICR1 = ((F_CPU / converter) / PRESCALE);
		OCR1A = (ICR1 / 2);
		// Change frequency, 50% square duty cycle
		printDec(converter);
		_delay_ms(10);
	}
}


ISR(TIMER1_CAPT_vect)
{
}


/***********************************************************************************
 HELPER FUNCTIONS
***********************************************************************************/


void swap(uint16_t* a, uint16_t* b)
{
	uint16_t temp = *a;
	*a = *b;
	*b = temp;
}

// Improved filter function:
uint16_t medFilter(uint16_t signal, uint16_t array[11])
{
	array[count] = signal;
	int i, j, min;
	for (i = 0; i < 11-1; i++) {
		min = i;
		for (j = i + 1; j < 11; j++) {
			if (array[j] < array[min])
				min = j;
		}
		swap(&array[min], &array[i]);
	}
	count++;
	if (count == 11)
		count = 0;

	return array[6];
}

void initADC(void)
{
	ADCSRA |= (1 << ADEN);
	ADMUX |= (1 << REFS0);
	ADCSRA |= (1 << ADPS2) | (1 << ADPS1);
	ADCSRA |= (1 << ADSC);
}

uint16_t analog(uint8_t channel)
{
	ADMUX &= 0xF0;
	ADMUX |= channel;
	ADCSRA |= (1 << ADSC);
	
	while (ADCSRA & (1 << ADSC));
	return ADC;
}

void initUART(unsigned int baud)
{
	UCSR0A = (1 << U2X0);

	// Enable interrupts each transmission:
	//USCR0B = (1 << TXCIE0);

	unsigned int ubrr = F_CPU / 8 / baud - 1;
	UBRR0H = (unsigned char)(ubrr >> 8);
	UBRR0L = (unsigned char)ubrr;

	UCSR0B = (1 << TXEN0);
	UCSR0C = (1 << UCSZ00) | (1 << UCSZ01);
}


void transmitByte(unsigned char data)
{
	while (!(UCSR0A & (1 << UDRE0)));
	UDR0 = data;
}


void printDec(int num) {
	char stringNumber[20];
	sprintf(stringNumber, "%d\r\n", num);
	transmitString(stringNumber);

}


void transmitString(char* StringPtr) {
	while (*StringPtr != 0x00) {
		transmitByte(*StringPtr);
		_delay_ms(10);
		StringPtr++;
	}
}                            
                        
To main website