Skip to content
Dec 6 14

Quadcopters

by Riscy

Well a while ago I went to a friends place and there was someone with a quadcopter and they had it hooked up to their laptop and mucking around with it. I thought that looks like great fun so I bought some stuff and made my own. I got all the parts from a combination of RCTimer and HobbyKing but I think there is some good advice required for people who want to do the same.

I chose the RCTimer Spider UAV quadcopter:
RCTimer Spider

This is a good medium size quadcopter which has the capability of having a gimbal added to it later. These are the things I learnt that hopefully will help others to make the right decisions for their first quadcopter.

The XT60 connectors seem to be the new standard for all battery connections. You will need to buy male and female connectors to put on your power distribution board and batteries:
XT60 Connectors

Use 3.5mm connectors between your motors and ESCs, they can come loose but it makes field repairs easier. Just make sure you check ALL connections before each flight.
3.5mm connectors

The screws that come with the RCTimer quadcopter are total rubbish, they have ruined at least 4 hex key drivers. I decided to use screws from Element14 (hex head and torx).
Cheap hex head
Stainless steel hex head
Torx head

You need heatshrink, lots of it and some wire as well so you can extend the length of the ESC cables. You should twist the ESC cables that run back to the power distribution board. This will reduce the magnetic interference. AWG16 should be sufficient for a quadcopter this size:
Black cable
Red cable
Get a range of different sizes

Clear heatshrink is good for protecting some components that you need to see LEDs on (transmitters and receivers etc). I’d go into Jaycar and pick some up that looks the right size. You don’t save that much from getting it off eBay.

Make sure you get some nyloc nuts for your props. It is guaranteed that the nuts will come off otherwise, perhaps midair. Check the tightness of props and nuts before every launch. I got my nyloc nuts from Mitre 10.

These are other parts you should buy from RCTimer as well:
Telemetry kit
Spare frame (you will need it)
LiPo bag
Battery straps
Battery monitor
Spare props (plastic is better to start with, it cuts less OUCH!)
Cable ties, you can never have too many
Power monitor (and power supply for APM)

These instructions are great for putting it all together:
Build photos

This is a great guide with more detailed instructions:
Build tutorial

Now for parts you want from HobbyKing, they have a warehouse in Australia so they can send LiPo batteries by truck. You will want batteries, a charger and a transmitter. Start cheap and then work up to good stuff if you want:
Transmitter
4000mAh 4S battery (get two)
Battery charger

You will need a DC power supply for the battery charger. Jaycar has some, they are more expensive than they should be and I’ve had two blow up (one DOA the other after 3 months). But I couldn’t find anything better in Hobart for the price.
Power supply

In my next post I’ll discuss hints on how to make the build go smoothly.

Oct 5 12

Power monitor update

by Riscy

Well after setting the original system up it only lasted 4 days before the battery went flat. It appears I shouldn’t leave the radio on all the time on the remote node otherwise it chews through the battery very quickly. So I searched through the JeeNode examples to look for low power options.

One of the issues with low power mode for the ATmega is that it doesn’t maintain time accuracy, and the JeeNode doesn’t have a RTC (I’m thinking of getting one). So the best thing I think I can do is to just turn the radio off when not used. The other option is to tone down update rate, I’ll probably look at that after I’ve collected more baseline data.

So after working with the RadioBlip code from the Jeelib examples I came up with this:

#include <Ports.h>
#include <RF12.h>
#include <avr/sleep.h>
#include “kWh.h”

#define SEND_MODE 1

volatile bool adcDone;

// for low-noise/-power ADC readouts, we’ll use ADC completion interrupts
ISR(ADC_vect) { adcDone = true; }

// this must be defined since we’re using the watchdog for low-power waiting
ISR(WDT_vect) { Sleepy::watchdogEvent(); }

static byte vccRead (byte count =4) {
set_sleep_mode(SLEEP_MODE_ADC);
ADMUX = bit(REFS0) | 14; // use VCC as AREF and internal bandgap as input
bitSet(ADCSRA, ADIE);
while (count– > 0) {
adcDone = false;
while (!adcDone)
sleep_mode();
}
bitClear(ADCSRA, ADIE);
// convert ADC readings to fit in one byte, i.e. 20 mV steps:
//  1.0V = 0, 1.8V = 40, 3.3V = 115, 5.0V = 200, 6.0V = 250
return (55U * 1024U) / (ADC + 1) – 50;
}

class Port;
Port inputPort(1);
static unsigned long last;
static unsigned long totalblinks;

void setup() {
Serial.begin(57600);
inputPort.mode2(INPUT); // Set AIO mode as input
inputPort.digiWrite2(1); // Activate pull-up resistor for AIO

//rf12_config();
rf12_config(); // Apparently this is necessary
rf12_easyInit(1); // Send value at most every 3 seconds
last = millis();
rf12_control(0xC040); // set low-battery level to 2.2V i.s.o. 3.1V
rf12_sleep(RF12_SLEEP);
}

void loop () {

static boolean ledOn = false; // Variable to indicate LED status
int data = inputPort.anaRead();
rf12_easyPoll();
if (!ledOn && data > 760) {        // After testing I found the switching point was 750
ledOn = true;
} else if (ledOn && data < 740) {
ledOn = false;
ledBlink();
totalblinks++;
}
}

void ledBlink() {
static int nBlinks = 0;
unsigned long time = millis();
unsigned long interval = time – last;

nBlinks++;
if (interval < 0) { // millis() overflow
last = time;
nBlinks = 0;
} else if (interval > 1000) { // 1+ sec passed
// Blinks are 1000 per kWh, or 1 Wh each
// One hour has 3.6M milliseconds
long watts = nBlinks * 1 * 3.6E6 / interval;

byte vcc = vccRead();
wattSend(watts,totalblinks,vcc);

last = time;
nBlinks = 0;
}
}

static void wattSend(long watts,long blinks, long vcc) {
Packet_t packet;
packet.lang = LANG_ELECTRICITY;
packet.mesg = MESG_ELEC_CURRENT;
packet.data = watts;
packet.blinks = blinks;
packet.vcc = vcc;
rf12_sleep(RF12_WAKEUP);
while (!rf12_canSend())
rf12_recvDone();
rf12_sendStart(0, &packet, sizeof packet);
rf12_sendWait(SEND_MODE);
delay(10);
rf12_sleep(RF12_SLEEP);
}

The code also contains parts which measures the Vcc voltage so I can determine when the battery pack is starting to get low.

The receiver code needed to change too. I wanted to include not just power, but the number of blinks and the new Vcc measurement. Here is the new code:

#include <Ports.h>
#include <RF12.h>
#include “kWh.h”

void setup() {
Serial.begin(57600);
//rf12_config();
rf12_config();
}

void loop() {
if (rf12_recvDone() && rf12_crc == 0 && rf12_len == sizeof (Packet_t)) {
Packet_t packet = *(Packet_t *) rf12_data;

if (packet.lang == LANG_ELECTRICITY) {
if (packet.mesg == MESG_ELEC_CURRENT) {
wattShow(packet.data,packet.blinks,packet.vcc);
}
}
}
else {
delay(10);
}
}

static void wattShow(long watts,long blinks,long vcc) {
Serial.print(watts);
Serial.print(“,”);
Serial.print(blinks);
Serial.print(“,”);
Serial.println(vcc);
}

I also changed the receiving Perl script to put the data into a MySQL database instead of dumping to a text file.

use strict;
use warnings;
use DBI;
use LWP::Simple;

use Win32::SerialPort qw( :STAT 0.19 );
use POSIX qw/strftime/;

my $port = Win32::SerialPort->new(‘COM9′);

my $time = time;

# MYSQL CONFIG VARIABLES
my $host = “192.168.1.101”;
my $database = “powermonitor”;
my $tablename = “data”;
my $user = “powermonitor”;
my $pw = [the password];

if( ! defined($port) ) {
die(“Can’t open COM9: $^E\n”);
}

my $outfd;
open ($outfd, “>>”, “log.txt”) or die “Failed to open output file – $!n”;

my $output = select(STDOUT);
$|++;
select($outfd);
$|++;
select $output;

$port->initialize();

$port->baudrate(57600);
$port->parity(‘none’);
$port->databits(8);
$port->stopbits(1);
$port->write_settings();
$port->are_match(“\n”);

while(1) {
my $char = $port->lookfor();
if ($char) {
$char =~ s/\xd//g;
if($char =~ m/(\d+),(\d+),(\d+)/){
my $dbh = DBI->connect(‘DBI:mysql:’.$database.';host=’.$host, $user, $pw, { RaiseError => 1});
my $thedate=strftime(‘%Y-%m-%d %H:%M:%S’,localtime);
my $pvdate = strftime(‘%Y%m%d’,localtime);
my $pvtime = strftime(‘%H:%M’,localtime);
my $pvoutput = “get http://pvoutput.org/service/r2/addoutput.jsp?key=[the api number]&sid=[the sid number]&d=$pvdate&pt=$pvtime&ip=$1″;

#my $content = get $pvoutput;

my $sth=$dbh->prepare(“INSERT INTO $tablename VALUES(‘$thedate’,’$1′,’$2′,’$3′)”)
or die “Can’t prepare SQL statement: $DBI::errstr\n”;
$sth->execute
or die “Can’t execute SQL statement: $DBI::errstr\n”;
$dbh->disconnect();
}
sleep (1);
}
}
$port->close();
exit(0);

This is the database structure:

— Server version: 5.5.24

— Database: `powermonitor`

— Table structure for table `data`

CREATE TABLE IF NOT EXISTS `data` (
`date` datetime NOT NULL,
`watts` int(11) NOT NULL,
`blinks` int(11) NOT NULL,
`vcc` int(11) NOT NULL,
PRIMARY KEY (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Now finally I wanted to graph the data but I haven’t found a good graph program for the front-end. So I’m using PVoutput, I’m not sure how it will work but I wrote this script to dump usage data for 5 minute intervals. It calculates averages and power usage data for the last 5 minute period, I initiate it using cron.

use strict;
use warnings;
use DBI;
use LWP::Simple;
use LWP::UserAgent;
use HTTP::Request::Common qw(POST);

use POSIX qw/strftime/;

# MYSQL CONFIG VARIABLES
my $host = “192.168.1.101”;
my $database = “powermonitor”;
my $tablename = “data”;
my $user = “powermonitor”;
my $pw = “powermonitor”;

my $thetime=int(time() / 300) * 300;
my $thetime_5=$thetime-300;
my $thetimestring=strftime(‘%Y-%m-%d %H:%M:%S’,localtime($thetime));
my $thetime_5string=strftime(‘%Y-%m-%d %H:%M:%S’,localtime($thetime_5));

my $dbh = DBI->connect(‘DBI:mysql:’.$database.';host=’.$host, $user, $pw, { RaiseError => 1});

my $sth=$dbh->prepare(“select DATE_FORMAT(MIN(date), ‘%Y-%m-%d’) as day, max(blinks)-min(blinks) as Wh, round(avg(watts)) as Watts, Date_format(Min(date), ‘%H:%i’) as time from data where date > ‘$thetime_5string’ AND date < ‘$thetimestring’ GROUP BY ( 12 * HOUR( date ) + FLOOR( MINUTE( date ) / 5 ));”)
or die “Can’t prepare SQL statement: $DBI::errstr\n”;
$sth->execute
or die “Can’t execute SQL statement: $DBI::errstr\n”;
my @result = $sth->fetchrow_array();
my $pvdate= substr($result[0], 0, 4).substr($result[0],5,2).substr($result[0],8,2);
my $pvwatth= $result[1];
my $pvwatt= $result[2];
my $pvtime= $result[3];
print “$pvdate $pvwatth $pvwatt $pvtime\n”;
$sth->finish();
$dbh->disconnect();

my $url = “http://pvoutput.org/service/r2/addstatus.jsp”;

my $ua = LWP::UserAgent->new( timeout => 120 );
$ua->default_header(‘X-Pvoutput-Apikey’=> “edaf9593007119df34f393acbd36703b7476457b”);
$ua->default_header(‘X-Pvoutput-SystemId’=> “12265”);

my $req = POST $url, [
d => $pvdate,
t => $pvtime,
v3=> $pvwatth,
v4=> $pvwatt
];

my $content = $ua->request($req)->as_string;

print $content;

I’ll post a link to my PVOutput data once it looks ok.

Optimization WordPress Plugins & Solutions by W3 EDGE