Skip to content

Instantly share code, notes, and snippets.

@guyc
Created May 3, 2012 03:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guyc/2582935 to your computer and use it in GitHub Desktop.
Save guyc/2582935 to your computer and use it in GitHub Desktop.
The file that is currently on an Arduino Uno with a serial number of 64932343738351217080
/*
Copyright (c) 2011 Guy Carpenter
*/
#include <Arduino.h>
#include "Command.h"
#define CMDBUF_LEN 40
void Command::skipWhitespace(char **line, int *len)
{
while (*len > 0 && **line == ' ') {
(*len)--;
(*line)++;
}
}
boolean Command::parseCommand(char **line, int *len, char *cmd)
{
boolean result = false;
skipWhitespace(line, len);
if (*len > 0) {
*cmd = **line;
(*len)--;
(*line)++;
result = true;
}
return result;
}
boolean Command::parseInteger(char **line, int *len, value_t *value)
{
boolean result = false;
*value = 0;
while (*len>0 && **line>='0' && **line<='9') {
*value = *value * 10 + (**line - '0');
(*len)--;
(*line)++;
result = true;
}
return result;
}
boolean Command::parseTuple(char **line, int *len, value_t *tuple)
{
boolean result = false;
skipWhitespace(line, len);
for (int i=0; i<COMMAND_TUPLE_LEN; i++) {
if (parseInteger(line, len, tuple+i+1)) {
result = true;
tuple[0] = i+1;
if (*len>0 && **line == ',') {
// found comma, so more integers expected
(*len)--;
(*line)++;
result = false; // need another int to succeed
}
else {
break;
}
}
else {
break;
}
}
return result;
}
boolean Command::parseLine(char *line, int len)
{
boolean result = false;
if (parseCommand(&line, &len, &command) &&
parseTuple(&line, &len, address) &&
parseTuple(&line, &len, value)) {
result = true;
}
else {
Serial.println("$Error Invalid command syntax");
}
return result;
}
boolean Command::parseInput()
{
boolean result = false;
static char line[CMDBUF_LEN]; // NOT zero terminated
static int len = 0;
if (Serial.available()) {
char c = Serial.read();
if (c==10 || c==13) {
result = parseLine(line, len);
len = 0;
}
else if (len < CMDBUF_LEN) {
line[len++] = c;
}
return result;
}
}
void Command::dump()
{
Serial.print(command);
Serial.print(" ");
for (int i=0;i<address[0];i++) {
if (i>0) Serial.print(",");
Serial.print(address[i+1]);
}
Serial.print(" ");
for (int i=0;i<value[0];i++) {
if (i>0) Serial.print(",");
Serial.print(value[i+1]);
}
Serial.println();
}
#ifndef Command_h
#define Command_h
#include <Arduino.h>
#define COMMAND_TUPLE_LEN 3
typedef unsigned int value_t;
class Command
{
public:
char command;
value_t address[COMMAND_TUPLE_LEN+1];
value_t value[COMMAND_TUPLE_LEN+1];
private:
void skipWhitespace(char**, int*);
boolean parseCommand(char **line, int *len, char *cmd);
boolean parseInteger(char **line, int *len, value_t *value);
boolean parseTuple(char **line, int *len, value_t *tuple);
boolean parseLine(char *line, int len);
public:
boolean parseInput();
void dump();
};
#endif
#include <SwitecX25.h>
#include "Command.h"
#include "RotaryEncoder.h"
#include "IntRotaryEncoder.h"
#include "LED.h"
#include "Switch.h"
/////////////////////////////////////
// 0 RX
// 1 TX
// 2 INT
// 3 PWM INT
// 4
// 5 PWM
// 6 PWM
// 7
//
// 8
// 9 PWM
// 10 PWM
// 11 PWM
// 12
// 13
/////////////////////////////////////
Command cmd;
// 6 steps per rotation, so 60 degrees per step of motor
// 180:1 gear ration
// 1/3 degree per step of needle
// 315 degrees full-scale-deflection of needle
// limit to 232 degrees sweep for square thermo dials
// at 3 steps per degree is 696 steps
#define MOTOR_STEPS (696)
#define MOTOR_OFF_POS (MOTOR_STEPS/2)
SwitecX25 motors[] = {
SwitecX25(MOTOR_STEPS, 4,5,6,7),
SwitecX25(MOTOR_STEPS, 8,9,11,12)
};
const unsigned int motorCount = sizeof(motors)/sizeof(*motors);
//RotaryEncoder encoder(2,3);
//IntRotaryEncoder encoder;
#define LED_BRIGHT 100
#define LED_DIM 20
#define LED_OFF 0
#define LED_PERIOD 1000
LED leds[] = {LED(3), LED(10)};
const unsigned int ledCount = sizeof(leds)/sizeof(*leds);
#define MOTOR_INACTIVE_TIMEOUT 5000
unsigned long motorUpdate[] = {0,0};
unsigned char motorActive[] = {false, false};
Switch switch1(A1);
Switch switch2(A0);
boolean active = true;
void setLedBrightness(int index)
{
unsigned char brightness = LED_OFF;
if (switch2.set) {
brightness = LED_OFF;
Serial.println("off - switch2 on");
} else if (!switch1.set) {
brightness = LED_OFF;
Serial.println("off - switch1 off");
} else if (motorActive[index]) {
brightness = LED_BRIGHT;
Serial.println("bright - active");
} else {
brightness = LED_DIM;
Serial.println("dim - inactive");
}
leds[index].set(brightness);
}
void setup(void) {
Serial.begin(9600);
Serial.print(motorCount);
Serial.print(" motors. ");
Serial.println("Go!");
// excercise the code to override the acceleration table.
// static unsigned short accelTable[][2] = {
// { 20, 3000},
// { 100, 1000},
// { 300, 600}
//};
//motors[1].accelTable = accelTable;
//motors[1].maxVel = accelTable[3-1][0];
leds[0].period = LED_PERIOD; // milliseconds to change
leds[1].period = LED_PERIOD;
}
void loop(void) {
/*
{
static int n = 0;
int delta = encoder.read();
if (delta!=0) {
n += delta;
Serial.print(delta);
Serial.print(" ");
Serial.println(n);
}
int pos = motors[1].targetStep + delta;
if (pos<0) pos=motors[1].steps-1;
if (pos>=motors[1].steps) pos=0;
motors[1].setPosition(pos);
}
*/
// switch1 on turns on light
if (switch1.changed()) {
Serial.print("switch1 ");
Serial.println(switch1.set ? "on" : "off");
setLedBrightness(0);
setLedBrightness(1);
}
// switch2 on turns everything off
if (switch2.changed()) {
Serial.print("switch2 ");
Serial.println(switch2.set ? "on" : "off");
active = !switch2.set;
if (switch2.set) {
// inactive
motors[0].zero();
motors[1].zero();
motors[0].setPosition(MOTOR_OFF_POS);
motors[1].setPosition(MOTOR_OFF_POS);
} else {
}
setLedBrightness(0);
setLedBrightness(1);
}
// if motors become inactive, dim leds
{
for (int i=0;i<motorCount;i++) {
if (motorActive[i]) {
if ((millis() - motorUpdate[i]) > MOTOR_INACTIVE_TIMEOUT) {
motorActive[i] = false;
setLedBrightness(i);
Serial.println("inactive");
}
}
}
}
motors[0].update();
motors[1].update();
leds[0].update();
leds[1].update();
if (cmd.parseInput()) {
int index = cmd.address[1];
//cmd.dump();
if (index<motorCount) {
SwitecX25 *motor = motors+index;
switch (cmd.command) {
case 'z':
motor->zero();
break;
case 's':
if (active) motor->setPosition(cmd.value[1]);
motorActive[index] = true;
motorUpdate[index] = millis();
setLedBrightness(index);
break;
case 'r':
// r <n> <steps> set motor range
motor->steps = cmd.value[1];
break;
case 'l':
{
LED *led = leds + cmd.address[1];
led->set(cmd.value[1]);
break;
}
case 'x':
motor->steps = 2000; // > 3 * 360
for (int i=0;i<cmd.value[1];i++) {
motor->currentStep = 0;
motor->setPosition(360*3);
//while (motor->currentStep < 360*3) motor->update();
while (!motor->stopped) motor->update();
}
motor->currentStep = 0;
motor->setPosition(0);
break;
}
}
}
}
#include <Arduino.h>
#include "IntRotaryEncoder.h"
// interrupt 0 services pin 3
#define INT0 0
#define PIN0 2
// interrupt 1 services pin 4
#define INT1 1
#define PIN1 3
volatile int _pos1 = 0;
volatile int _pos2 = 0;
volatile int _lastPosition = 0;
// use separate counts because we handle two interrupts here
// and it appears they can overlap.
static void intRotaryEncoderISR()
{
static byte lastData = 0x3;
byte data = (PIND >> 2) & 0x3; // pin3 and pin2 data
byte trigger = (lastData ^ data);
lastData = data;
if (trigger == 0x1) { // pin0 changed
_pos1 += ((data & 1) ^ data>>1) ? 1 : -1;
} else if (trigger == 0x2) { // pin1 changed
_pos2 += ((data & 1) ^ data>>1) ? -1 : 1;
}
}
IntRotaryEncoder::IntRotaryEncoder()
{
pinMode(PIN0, INPUT);
digitalWrite(PIN0, HIGH); // enable internal pull-up resistors to avoid floating inputs
pinMode(PIN1, INPUT);
digitalWrite(PIN1, HIGH); // enable internal pull-up resistors to avoid floating inputs
attachInterrupt(INT0, intRotaryEncoderISR, CHANGE);
attachInterrupt(INT1, intRotaryEncoderISR, CHANGE);
}
int IntRotaryEncoder::read()
{
int position = _pos1 + _pos2;
int delta = position - _lastPosition;
_lastPosition = position;
return delta;
}
#ifndef _IntRotaryEncoder_h_
#define _IntRotaryEncoder_h_
// Since the standard arduino only has interrupts for pins 2 and 3
// the rotary encoder MUST be attached to those two pins.
class IntRotaryEncoder
{
public:
IntRotaryEncoder();
int read(); // returns signed delta
};
#endif
#include <Arduino.h>
#include "LED.h"
LED::LED(unsigned char pin)
{
this->pin = pin;
this->time0 = 0;
this->target = 0;
this->value = 0;
this->period = 0; // instant
pinMode(pin, OUTPUT);
analogWrite(pin, 0);
}
void LED::set(unsigned char value)
{
if (this->period==0) {
this->value = this->target = value;
analogWrite(pin, this->value);
} else {
this->time0 = millis();
this->target = value;
}
}
void LED::update()
{
if (this->value != this->target) {
unsigned long delta = millis() - time0;
unsigned char newValue;
if (delta>=period) {
Serial.print(delta);
Serial.println(" done");
newValue = this->value = this->target; // done
} else {
int dv = (int)target - (int)value;
// cast to long because dv * d will exceed 16-bit signed int.
int dvt = (long)dv * (long)delta / (long)period;
newValue = (int)value + dvt;
}
analogWrite(pin, newValue);
}
}
#ifndef LED_h
#define LED_h
class LED
{
public:
unsigned char pin;
unsigned char target;
unsigned char value;
unsigned long period;
unsigned long time0;
LED(unsigned char pin);
void set(unsigned char value);
void update();
};
#endif
#include <Arduino.h>
#include "RotaryEncoder.h"
RotaryEncoder::RotaryEncoder(unsigned char pin1, unsigned char pin2)
{
this->pin1 = pin1;
this->pin2 = pin2;
pinMode(pin1, INPUT);
digitalWrite(pin1, HIGH); // enable internal pull-up resistors to avoid floating inputs
pinMode(pin2, INPUT);
digitalWrite(pin2, HIGH);
}
int RotaryEncoder::read()
{
int delta = 0;
static unsigned char lastData = 0;
unsigned char data = (PIND >> 2) & 0x3;
unsigned char change = data ^ lastData;
if (change & 0x1) { // pin0 changed
delta += ((data & 1) ^ data>>1) ? +1 : -1;
} else if (change & 0x2) { // pin1 changed
delta += ((data & 1) ^ data>>1) ? -1 : +1;
}
lastData = data;
return delta;
}
#ifndef RotaryEncoder_h
#define RotaryEncoder_h
// Sequence moving forward:
// 1 1 << stable state
//
// 1 1 << moving forward
// 1 0
// 0 0
// 0 1
// 1 1
//
// 1 1 << moving backwards
// 0 1
// 0 0
// 1 0
// 1 1
//
// FWD: 3 1 0 2 3
// was 0 now 1 : fwd
// was 1 now 3 : fwd
// was 2 now 0 : fwd
// was 3 now 2 : fwd
//
// REV: 3 2 0 1 3
// was 0 now 2 : rev
// was 1 now 0 : rev
// was 2 now 3 : rev
// was 3 now 1 : rev
//
//
class RotaryEncoder
{
public:
unsigned char pin1;
unsigned char pin2;
RotaryEncoder(unsigned char pin1, unsigned char pin2);
int read();
};
#endif
#include <Arduino.h>
#include "Switch.h"
Switch::Switch(unsigned char pin)
{
this->pin = pin;
this->set = false;
pinMode(pin, INPUT); // set pin to input
digitalWrite(pin, HIGH); // turn on pullup resistors
}
boolean Switch::isSet()
{
return 0==digitalRead(this->pin);
}
// if changed returns true, check sw.set for new value
boolean Switch::changed()
{
boolean wasSet = this->set;
this->set = this->isSet();
return (this->set != wasSet);
}
#ifndef Switch_h
#define Switch_h
class Switch
{
public:
unsigned char pin;
boolean set;
Switch(unsigned char pin);
boolean isSet();
boolean changed();
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment