Showing posts with label AVR. Show all posts
Showing posts with label AVR. Show all posts

21 Apr 2013

Elevon / V-Tail mixer with Arduino Pro Mini 5V



I got this new and and really handy r/c transmitter from multiplex: the SMART SX.



It's really a minimal approach. I have been a fan of multiplex for a long time because they have always been pioneers in their domain. This one is the first quality r/c transmitter with a gamepad layout (there are many technical features other will copy sooner or later but I bought it because of the layout primarily).

However, this being an entry level transmitter it is really limited in functionality. It has all the features a modern transmitter needs to have, configurable through 1 (yes ONE) button!

However, Elevon/Y-tail mixing isn't a feature baked in. I needed an elevon mixer for my new flying wing and couldn't get one quickly. Fortunately I have some unused micro controller boards laying around which just waited for this hack.


A 15$ Arduino Pro mini 5V seemed to be suitable (although overkill, I just needed something in a short time). Using 2 input interrupts  1 timer for the Servo.H library and 2 PWM pins as output, there is still a lot of unused hardware.

Code

/**
PPM V-Tail/elevon mixer

Input pins:
 2: aileron/rudder
 3: elevator

Output pins:
 9: servo left
10: servo right
*/


#include <math.h>
#include <Servo.h>

//#define DEBUG
#define START_OUTPUT_PIN 9

int Chan1Interrupt = 0; // pin 2, aileron
int Chan2Interrupt = 1; // pin 3, elevator

unsigned long Chan1_startPulse, Chan2_startPulse;
volatile double Chan1_val, Chan2_val = 1500;
volatile double Chan1_val_last, Chan2_val_last = 1500;
long StartMillis=0;
long FrameCounter=0;

Servo ServoArray[2];

double reverse(double val) {
 return (val-1500) * -1 + 1500;
}

double add(double val1, double val2) {
 double out = (val1 -1500 + val2 -1500) + 1500;
 out = (out < 1000) ? 1000 : out;
 out = (out > 2000) ? 2000 : out;
 return out;
}

void serviceServos(long pFrameCounter) {
 
 // do the v-tail mixing 
 double left  = add(Chan2_val, Chan1_val);
 double right = add(reverse(Chan2_val), Chan1_val);
 
 ServoArray[0].writeMicroseconds(left);
 ServoArray[1].writeMicroseconds(right);
}

void setup() {
 attachInterrupt(Chan1Interrupt, Chan1_begin, RISING);
 attachInterrupt(Chan2Interrupt, Chan2_begin, RISING);
 
 ServoArray[0].attach(START_OUTPUT_PIN+0, 900, 2100); // aileron
 ServoArray[1].attach(START_OUTPUT_PIN+1, 900, 2100); // elevator

 StartMillis = millis();
#ifdef DEBUG
 Serial.begin(115200);
#endif
}

void loop() {
 long LocalMillis;
 long LocalFrameCounter;
 LocalMillis = millis();
 LocalFrameCounter = (LocalMillis - StartMillis) / 20;

 if (LocalFrameCounter > FrameCounter) {
  FrameCounter = LocalFrameCounter;
  serviceServos(FrameCounter);
 }
}

void Chan1_begin() {
 Chan1_startPulse = micros();
 detachInterrupt(Chan1Interrupt);
 attachInterrupt(Chan1Interrupt, Chan1_end, FALLING);
}

void Chan1_end() {
 Chan1_val = micros() - Chan1_startPulse;
 detachInterrupt(Chan1Interrupt);
 attachInterrupt(Chan1Interrupt, Chan1_begin, RISING);
 if (Chan1_val < 1000 || Chan1_val > 2000) {
  Chan1_val = Chan1_val_last;
 } else {
  Chan1_val_last = Chan1_val;
 }
}

void Chan2_begin() {
 Chan2_startPulse = micros();
 detachInterrupt(Chan2Interrupt);
 attachInterrupt(Chan2Interrupt, Chan2_end, FALLING);
}

void Chan2_end() {
 Chan2_val = micros() - Chan2_startPulse;
 detachInterrupt(Chan2Interrupt);
 attachInterrupt(Chan2Interrupt, Chan2_begin, RISING);
 if (Chan2_val < 1000 || Chan2_val > 2000) { 
  Chan2_val = Chan2_val_last;
 } else {
  Chan2_val_last = Chan2_val;
 }
}

13 Feb 2013

Problems programming ATtiny85 from Arduino IDE

After several successful firmware uploads from the Arduino IDE via Arduino Uno as ISP programmer to an ATtiny85 it suddenly stopped to work.

I have not figured out why this happens, a friend of mine has the same problem and couldn't find what caused it neither.

Long story short, the work around is to use Arduino IDE to compile the sketch and avrdude directly to do the uploading.

Compiling

Compiling code for ATtiny85/45 in Arduino IDE requires that the hardware definitions of these chips are installed in Arduino IDE:

Once the code is compiled it can be found in /tmp/build[0-9]*.tmp/*.hex on a *nix platform (C:\Windows\temp\... probably on windows, check the Arduino IDE manual).

Once the location of the hex file is known, I copy it to a safe place (data in the temp folder are purged occasionally).

Arduino as ISP

I am using Arduino Uno as programmer. For this wot work the arduino must be flashed with an already available sketch called: ArduinoISP (found under Examples).

Once this sketch is uploaded, the Arduino Uno works as In System Programmer and can be hooked up to your ATtiny device. The Arduino Uno is the used as "bridge" between your USB port on the computer and the tiny, as well as power supply.


Uploading

Using avrdude with the following parameters worked out of the box for me on a virgin ATTiny85 from linux:
$ avrdude -P /dev/ttyACM0 -p t85 -c avrisp -b 19200 \
          -v -F -U flash:w:pulse_in.cpp.hex



16 Jan 2013

APM Camara control print

The first prototype, lets put this into a drone.


15 Jan 2013

APM Camera power control & CHDK intervalometer

Finally I made some progress on the r/c camera control. A fairly simple and cheap solution is to solder cables to the power button of a cannon camera. Hook these up to an ATtiny85 (45 would probably do as well). The ATtiny reads a PPM signal from an r/c receiver and switches the camera on or off accordingly.

ATtiny  pin layout:


Schema:


Demo:





ATTiny Code (C++ Arduino):

/**
 * Read PPM signal
 * 
 * Decode r/c receiver PPM servo signal and turn lights on 
 * according to stick position.
 *
 * $Id$
 */

// pin setup
#define PIN_PPM  0
#define PIN_POS1 4

unsigned long duration, lastgood = 0;
int position = 0;

void setup() {
  pinMode(PIN_PPM, INPUT);
  pinMode(PIN_POS1, OUTPUT);
  digitalWrite(PIN_POS1, LOW);
}

void loop() {
  // the length of the pulse (in microseconds) or 0 if no pulse 
  // started before the timeout (unsigned long)
  duration = pulseIn(PIN_PPM, HIGH, 20000); 
  if (duration == 0)
    duration = lastgood;
  else
    lastgood = duration;
  
  position = map(lastgood, 1000, 2000, 0, 1);
  
  if (position > 0)
    digitalWrite(PIN_POS1, HIGH);
  else
    digitalWrite(PIN_POS1, LOW);
}



Intervalometer Code (lua):

--[[
rem 2013-01-13 by Simon Wunderlin, ResearchDrones LLC
@title ResearchDrones Fast Interval
@param a = interval (sec/10)
@default a 10
--]]

function camera_close()
 click "display"
 sleep(1000)
 click "display"
 sleep(1000)
 shut_down()
 sleep(5000)
end

--[[
f = get_usb_power(1)
if f == 1 then camera_close() end 
]]--
--shoot()

repeat
 start = get_tick_count()
 
 press("shoot_half")
 repeat
  sleep(50)
  until get_shooting() == true
 click("shoot_full")
 release("shoot_half")
 
 sleep(a*100 - (get_tick_count() - start))
until ( false )

11 Aug 2012

Reading PPM Signal with arduino

I want to trigger additional logic in an UAV when I flip a 3 position switch on my R/C transmitter. This sketch will decode a PPM signal from an R/C receiver and will turn on two LEDs depending on switch position.

The code compiles for a Sparkfun Pro Micro 16MHz and also on an ATtiny85:


/**
 * Read PPM signal
 * 
 * Decode r/c receiver PPM servo signal and turn lights on 
 * according to servo position.
 *
 * $Id$
 */

// from which pin to read.
#define PIN_PPM   9
#define PIN_POS1 10
#define PIN_POS2 16

unsigned long duration, lastgood = 0;
int position = 0;

void setup() {
  pinMode(PIN_PPM, INPUT);
  pinMode(PIN_POS1, OUTPUT);
  pinMode(PIN_POS2, OUTPUT);
  digitalWrite(PIN_POS1, LOW);
  digitalWrite(PIN_POS2, LOW);
}

void loop() {
  // the length of the pulse (in microseconds) or 0 if no pulse 
  // started before the timeout (unsigned long)
  duration = pulseIn(PIN_PPM, HIGH, 20000); 
  if (duration == 0) {
    duration = lastgood;
  } else {
    lastgood = duration;
  }
  // map ppm values to 0, 1 and 2
  position = map(lastgood, 1000, 2000, 0, 2);
  // 0 == both off, 1 == LED1 on, LED2 off, 2 = both on 
  if (position > 0) {
    digitalWrite(PIN_POS1, HIGH);
  } else {
    digitalWrite(PIN_POS1, LOW);
  }
  if (position > 1) {
    digitalWrite(PIN_POS2, HIGH);
  } else {
    digitalWrite(PIN_POS2, LOW);
  }
}