这是一个和宋庆龄基金会和航天什么什么合作的项目,然后主要的功能是这样的。
通过433无线串口做图传(传一个图片要用20多秒,不要问我为什么要做……)然后可以在processing上控制Arduino无线传输图片或者陀螺仪,地磁,温度,GPS等传感器数据,控制太阳能帆板的张开还是闭合。Arduino还可以用太阳能充电,做电源管理。
然后这是代码
Arduino
#include "High_Temp.h"
#include "Wire.h"
#include "I2Cdev.h"
#include "MPU9250.h"
#include
#include
#include
#include
//Camera vary
#define PIC_PKT_LEN 128
#define PIC_FMT_VGA 7
#define PIC_FMT_CIF 5
#define PIC_FMT_OCIF 3
#define CAM_ADDR 0
#define CAM_SERIAL Serial1
#define PIC_FMT PIC_FMT_VGA
#define SERVO_PIN 10
#define SERVO1_PIN 6
#define sample_num_mdate 5000
// SD card shield
File myFile;
const byte cameraAddr = (CAM_ADDR << 5); // addr
unsigned long picTotalLen = 0; // picture length
//High Tempature Sensor vary
HighTemp ht(A4, A5);
//GPS vary
#define GPS_SERIAL Serial2
//GYRO vray
MPU9250 accelgyro;
I2Cdev I2C_M;
uint8_t buffer_m[6];
int16_t ax, ay, az;
int16_t gx, gy, gz;
int16_t mx, my, mz;
float heading;
float tiltheading;
float Axyz[3];
float Gxyz[3];
float Mxyz[3];
volatile float mx_sample[3];
volatile float my_sample[3];
volatile float mz_sample[3];
static float mx_centre = 0;
static float my_centre = 0;
static float mz_centre = 0;
volatile int mx_max =0;
volatile int my_max =0;
volatile int mz_max =0;
volatile int mx_min =0;
volatile int my_min =0;
volatile int mz_min =0;
// Servo
Servo myservo,myservo1;
void setup() {
//Serial init
Serial.begin(9600);
Serial.println("init....");
//High Temperature Sensor init
ht.begin();
Serial.println("High Temperature Sensor init suceed");
//GPS init
GPS_SERIAL.begin(9600);
Serial.println("GPS init succeed");
//gyro init
Wire.begin();
accelgyro.initialize();
Serial.println(accelgyro.testConnection() ? "MPU9250 connection successful" : "MPU9250 connection failed");
//servo init
myservo.attach(10);
myservo1.attach(6);
//Camera init
CAM_SERIAL.begin(115200);
camera_initialize();
//SD carc shield init
pinMode(4,OUTPUT); // CS pin of SD Card Shield
if (!SD.begin(4))
{
Serial.print("sd init failed");
return;
}
Serial.println("sd init done.");
}
void loop() {
//send High Tempature Sensor data
Serial.print("High Temperature Sensor = ");
Serial.println(ht.getThmc());
Serial.println();
//send GPS data
while(1){
char inChar = GPS_SERIAL.read();
while(inChar != '$'){ //'$' means a new line of data
inChar = GPS_SERIAL.read();
}
GPS_SERIAL.setTimeout(100);
String inString = GPS_SERIAL.readStringUntil('\n'); //read the whole line of data
if(inString.substring(0,5) == "GPGGA"){ //"GPGGA" is a line of message which include the message of possition, then send it with Serial
Serial.println(inString);
break;
}
}
Serial.println();
//send gyro data
getAccel_Data();
getGyro_Data();
getCompassDate_calibrated(); // compass data has been calibrated here
getHeading(); //before we use this function we should run 'getCompassDate_calibrated()' frist, so that we can get calibrated data ,then we can get correct angle .
getTiltHeading();
Serial.print("Acceleration(g) of X,Y,Z:");
Serial.print(Axyz[0]);
Serial.print(",");
Serial.print(Axyz[1]);
Serial.print(",");
Serial.println(Axyz[2]);
Serial.print("Gyro(degress/s) of X,Y,Z:");
Serial.print(Gxyz[0]);
Serial.print(",");
Serial.print(Gxyz[1]);
Serial.print(",");
Serial.println(Gxyz[2]);
Serial.print("Compass Value of X,Y,Z:");
Serial.print(Mxyz[0]);
Serial.print(",");
Serial.print(Mxyz[1]);
Serial.print(",");
Serial.println(Mxyz[2]);
Serial.println("The clockwise angle between the magnetic north and X-Axis:");
Serial.print(heading);
Serial.println(" ");
Serial.println("The clockwise angle between the magnetic north and the projection of the positive X-Axis in the horizontal plane:");
Serial.println(tiltheading);
Serial.println();
Serial.println(); //finished the whole data once, send some blank line
Serial.println();
Serial.println();
if (Serial.available()) { // if there is some data from Serial
int val = Serial.read(); // read it and store it in val
if(val == '2'){ //'2' is a valid char, flush the data of Serial buffer and wait the next command
Serial.flush();
while(Serial.available() <= 0){ //wait the next command from Serial, PC won't send the next command before it get ready for the next thing like quite a lot data of the picature.
}
val = Serial.read();
switch (val) {
case '2': //'2' means need get a picature and send it with Serial
preCapture(); //check the sync with camera
Capture(); //camera will get an image and store in its temporary memory
GetData(); //get the data from camera and store them as a jpg file in SD card.
sendData(); //send the data with Serial
break;
case '3': //'3' means need pull up the solar battery
myservo.write(180);
myservo1.write(170);
break;
case '4': //'4' means need pull down the solar battery
myservo.write(90);
myservo1.write(80);
break;
}
}
}
delay(1000);
}
/**
* Gyro functions
*/
void getHeading(void)
{
heading=180*atan2(Mxyz[1],Mxyz[0])/PI;
if(heading <0) heading +=360;
}
void getTiltHeading(void)
{
float pitch = asin(-Axyz[0]);
float roll = asin(Axyz[1]/cos(pitch));
float xh = Mxyz[0] * cos(pitch) + Mxyz[2] * sin(pitch);
float yh = Mxyz[0] * sin(roll) * sin(pitch) + Mxyz[1] * cos(roll) - Mxyz[2] * sin(roll) * cos(pitch);
float zh = -Mxyz[0] * cos(roll) * sin(pitch) + Mxyz[1] * sin(roll) + Mxyz[2] * cos(roll) * cos(pitch);
tiltheading = 180 * atan2(yh, xh)/PI;
if(yh<0) tiltheading +=360;
}
void Mxyz_init_calibrated ()
{
Serial.println(F("Before using 9DOF,we need to calibrate the compass frist,It will takes about 2 minutes."));
Serial.print(" ");
Serial.println(F("During calibratting ,you should rotate and turn the 9DOF all the time within 2 minutes."));
Serial.print(" ");
Serial.println(F("If you are ready ,please sent a command data 'ready' to start sample and calibrate."));
while(!Serial.find("ready"));
Serial.println(" ");
Serial.println("ready");
Serial.println("Sample starting......");
Serial.println("waiting ......");
get_calibration_Data ();
Serial.println(" ");
Serial.println("compass calibration parameter ");
Serial.print(mx_centre);
Serial.print(" ");
Serial.print(my_centre);
Serial.print(" ");
Serial.println(mz_centre);
Serial.println(" ");
}
void get_calibration_Data ()
{
for (int i=0; i=mx_sample[1])mx_sample[1] = mx_sample[2];
if (my_sample[2]>=my_sample[1])my_sample[1] = my_sample[2]; //find max value
if (mz_sample[2]>=mz_sample[1])mz_sample[1] = mz_sample[2];
if (mx_sample[2]<=mx_sample[0])mx_sample[0] = mx_sample[2];
if (my_sample[2]<=my_sample[0])my_sample[0] = my_sample[2];//find min value
if (mz_sample[2]<=mz_sample[0])mz_sample[0] = mz_sample[2];
}
mx_max = mx_sample[1];
my_max = my_sample[1];
mz_max = mz_sample[1];
mx_min = mx_sample[0];
my_min = my_sample[0];
mz_min = mz_sample[0];
mx_centre = (mx_max + mx_min)/2;
my_centre = (my_max + my_min)/2;
mz_centre = (mz_max + mz_min)/2;
}
void get_one_sample_date_mxyz()
{
getCompass_Data();
mx_sample[2] = Mxyz[0];
my_sample[2] = Mxyz[1];
mz_sample[2] = Mxyz[2];
}
void getAccel_Data(void)
{
accelgyro.getMotion9(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz);
Axyz[0] = (double) ax / 16384;//16384 LSB/g
Axyz[1] = (double) ay / 16384;
Axyz[2] = (double) az / 16384;
}
void getGyro_Data(void)
{
accelgyro.getMotion9(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz);
Gxyz[0] = (double) gx * 250 / 32768;//131 LSB(¡ã/s)
Gxyz[1] = (double) gy * 250 / 32768;
Gxyz[2] = (double) gz * 250 / 32768;
}
void getCompass_Data(void)
{
I2C_M.writeByte(0x0C, 0x0A, 0x01); //enable the magnetometer
delay(10);
I2C_M.readBytes(0x0C, 0x03, 6, buffer_m);
mx = ((int16_t)(buffer_m[1]) << 8) | buffer_m[0] ;
my = ((int16_t)(buffer_m[3]) << 8) | buffer_m[2] ;
mz = ((int16_t)(buffer_m[5]) << 8) | buffer_m[4] ;
//Mxyz[0] = (double) mx * 1200 / 4096;
//Mxyz[1] = (double) my * 1200 / 4096;
//Mxyz[2] = (double) mz * 1200 / 4096;
Mxyz[0] = (double) mx * 4800 / 8192;
Mxyz[1] = (double) my * 4800 / 8192;
Mxyz[2] = (double) mz * 4800 / 8192;
}
void getCompassDate_calibrated ()
{
getCompass_Data();
Mxyz[0] = Mxyz[0] - mx_centre;
Mxyz[1] = Mxyz[1] - my_centre;
Mxyz[2] = Mxyz[2] - mz_centre;
}
/**
* camera functions
*/
void clearRxBuf()
{
while (CAM_SERIAL.available())
{
CAM_SERIAL.read();
}
}
void sendCmd(char cmd[], int cmd_len)
{
for (char i = 0; i < cmd_len; i++) CAM_SERIAL.print(cmd[i]);
}
void camera_initialize()
{
char cmd[] = {0xaa,0x0d|cameraAddr,0x00,0x00,0x00,0x00} ;
unsigned char resp[6];
CAM_SERIAL.setTimeout(500);
while (1)
{
//clearRxBuf();
sendCmd(cmd,6);
if (CAM_SERIAL.readBytes((char *)resp, 6) != 6)
{
continue;
}
if (resp[0] == 0xaa && resp[1] == (0x0e | cameraAddr) && resp[2] == 0x0d && resp[4] == 0 && resp[5] == 0)
{
if (CAM_SERIAL.readBytes((char *)resp, 6) != 6) continue;
if (resp[0] == 0xaa && resp[1] == (0x0d | cameraAddr) && resp[2] == 0 && resp[3] == 0 && resp[4] == 0 && resp[5] == 0) break;
}
}
cmd[1] = 0x0e | cameraAddr;
cmd[2] = 0x0d;
sendCmd(cmd, 6);
// Serial.println("\nCamera initialization done.");
}
void preCapture()
{
char cmd[] = { 0xaa, 0x01 | cameraAddr, 0x00, 0x07, 0x00, PIC_FMT };
unsigned char resp[6];
CAM_SERIAL.setTimeout(100);
while (1)
{
clearRxBuf();
sendCmd(cmd, 6);
if (CAM_SERIAL.readBytes((char *)resp, 6) != 6) continue;
if (resp[0] == 0xaa && resp[1] == (0x0e | cameraAddr) && resp[2] == 0x01 && resp[4] == 0 && resp[5] == 0) break;
}
}
void Capture()
{
char cmd[] = { 0xaa, 0x06 | cameraAddr, 0x08, PIC_PKT_LEN & 0xff, (PIC_PKT_LEN>>8) & 0xff ,0};
unsigned char resp[6];
CAM_SERIAL.setTimeout(100);
while (1)
{
clearRxBuf();
sendCmd(cmd, 6);
if (CAM_SERIAL.readBytes((char *)resp, 6) != 6) continue;
if (resp[0] == 0xaa && resp[1] == (0x0e | cameraAddr) && resp[2] == 0x06 && resp[4] == 0 && resp[5] == 0) break;
}
cmd[1] = 0x05 | cameraAddr;
cmd[2] = 0;
cmd[3] = 0;
cmd[4] = 0;
cmd[5] = 0;
while (1)
{
clearRxBuf();
sendCmd(cmd, 6);
if (CAM_SERIAL.readBytes((char *)resp, 6) != 6) continue;
if (resp[0] == 0xaa && resp[1] == (0x0e | cameraAddr) && resp[2] == 0x05 && resp[4] == 0 && resp[5] == 0) break;
}
cmd[1] = 0x04 | cameraAddr;
cmd[2] = 0x1;
while (1)
{
clearRxBuf();
sendCmd(cmd, 6);
if (CAM_SERIAL.readBytes((char *)resp, 6) != 6) continue;
if (resp[0] == 0xaa && resp[1] == (0x0e | cameraAddr) && resp[2] == 0x04 && resp[4] == 0 && resp[5] == 0)
{
CAM_SERIAL.setTimeout(1000);
if (CAM_SERIAL.readBytes((char *)resp, 6) != 6)
{
continue;
}
if (resp[0] == 0xaa && resp[1] == (0x0a | cameraAddr) && resp[2] == 0x01)
{
picTotalLen = (resp[3]) | (resp[4] << 8) | (resp[5] << 16);
// Serial.print("picTotalLen:");
delay(500);
Serial.println(picTotalLen);
break;
}
}
}
}
void GetData()
{
unsigned int pktCnt = (picTotalLen) / (PIC_PKT_LEN - 6);
if ((picTotalLen % (PIC_PKT_LEN-6)) != 0) pktCnt += 1;
char cmd[] = { 0xaa, 0x0e | cameraAddr, 0x00, 0x00, 0x00, 0x00 };
unsigned char pkt[PIC_PKT_LEN];
char picName[] = "pic.jpg"; //make a jpg file named "pic.jpg", the file will be removed if it has already exist.
if (SD.exists(picName))
{
SD.remove(picName);
}
myFile = SD.open(picName, FILE_WRITE);
if(myFile){
CAM_SERIAL.setTimeout(1000);
for (unsigned int i = 0; i < pktCnt; i++)
{
cmd[4] = i & 0xff;
cmd[5] = (i >> 8) & 0xff;
int retry_cnt = 0;
retry:
delay(10);
clearRxBuf();
sendCmd(cmd, 6);
uint16_t cnt = CAM_SERIAL.readBytes((char *)pkt, PIC_PKT_LEN);
unsigned char sum = 0;
for (int y = 0; y < cnt - 2; y++)
{
sum += pkt[y];
}
if (sum != pkt[cnt-2])
{
if (++retry_cnt < 100) goto retry;
else break;
}
myFile.write((const uint8_t *)&pkt[4], cnt-6); //write the date getted from camera.
}
cmd[4] = 0xf0;
cmd[5] = 0xf0;
sendCmd(cmd, 6);
}
myFile.close();
}
void sendData(){
byte dataBuffer[127];
int tail = 0;
delay(1000);
File photoFile = SD.open("pic.jpg");
if (photoFile) { //send 128 bytes once a time,127 bytes from the jpg file, and calc 1 check byte(sum check)
while (photoFile.position() < photoFile.size()) { //do when there is bytes in jpg file.
dataBuffer[tail++] = photoFile.read(); //fullfill the databuffer
if(tail == 127){ //if already get 127 byte from jpg file
int val;
do{
int sum = 0;
for(int i=0; i< 127; i++){
Serial.write(dataBuffer[i]); // send the data in buffer
sum += dataBuffer[i];
sum = sum % 0xFF; //calc the check byte.
}
Serial.write(sum); //send the check byte.
tail = 0;
while(Serial.available() <= 0){
}
val = Serial.read(); //get a command to know if PC have got all the 128 bytes correctly.
if(val == '3'){ //'3' is a special command in case of the pc have restart the program, but arduino are still trying to send all the camera data. it should be finished.
return;
}
} while (val == '0'); //'0' means pc didn't get the bytes correctly, something is wrong, resend it.
}
}
if(tail > 0){ //if there are still bytes in jpg file, but less than 127byte. most thing is similar to before expect need fullfill the buffer when there is no data in jpg file.
int val;
do{
int sum = 0;
for(int i=0; i< 127; i++){
if(i < tail){
Serial.write(dataBuffer[i]);
sum += dataBuffer[i];
sum = sum % 0xFF;
}else{
Serial.write(0); //if there are no bytes in jpg file, fill it with 0x00;
}
}
Serial.write(sum);
while(Serial.available() <= 0){
}
val = Serial.read();
} while (val == '0');
}
}else{
Serial.println("open photoFile failed");
}
photoFile.close();
}
processing
import processing.serial.*;
Serial myPort;
OutputStream output;
int is_first_change_to_system_state_1 = 0; //something special should be done at first when system_state is changed to 1
int number_got_of_img_now = -1;
String inString = "0"; //this is a temp vary to store the date from the Serial.
int pic_length; //how many bytes the img include
int system_state = 0; //this vary indicate which state is the program running now.
PImage img;
void setup() {
size(800, 600);
printArray( Serial.list() );
myPort = new Serial( this, Serial.list()[0], 9600); //LOOK HERE!!!!!!!!!!!!!!!!!!!!!!!! 0 is the number of Serial module on my PC, you NEED change it to a correct number, most time is 0 or 1 in PC.
myPort.clear();
println("Processing init finished");
}
void draw() {
if (system_state == 1) {
while ( myPort.available () > 0 ) {
if (is_first_change_to_system_state_1 == 0) { // Arduino will send the length of img at first, get it
delay(500);
inString = myPort.readStringUntil('\n');
inString = trim(inString);
pic_length = int(inString);
println("pic_length: "+pic_length);
if (pic_length == 0) {
system_state = 0;
break;
}
is_first_change_to_system_state_1 = 1;
}else {
int[] dataBuffer = new int[128];
int sum = 0;
boolean numberEnoughFlag = true;
for (int i = 0; i < 127; ++i) {
if (numberEnoughFlag) {
if (Get_Valid_Serial_Number() == false) { //if get enough data from serial
numberEnoughFlag = false;
}
if (numberEnoughFlag) {
dataBuffer[i] = myPort.read(); //get the data bytes from serial
sum += dataBuffer[i];
sum = sum % 0xFF;
//println("i = "+i+" , dataBuffer[i] = "+dataBuffer[i]);
}
}
}
if (numberEnoughFlag) {
if (Get_Valid_Serial_Number() == false) {
numberEnoughFlag = false;
}
dataBuffer[127] = myPort.read(); //get the check byte
}
while ((sum != dataBuffer[127]) || (!numberEnoughFlag)) { //if check byte is incorrect or not enough bytes received, get bytes again
myPort.write('0'); //send '0' to tell the arduino something is wrong during communication
sum = 0;
numberEnoughFlag = true;
int falseNumber = 0;
for (int i = 0; i < 127; ++i) {
if (numberEnoughFlag) {
if (Get_Valid_Serial_Number() == false) {
numberEnoughFlag = false;
}
if (numberEnoughFlag) {
dataBuffer[i] = myPort.read();
sum += dataBuffer[i];
sum = sum % 0xFF;
falseNumber = i;
}
}
}
if (numberEnoughFlag) {
if (Get_Valid_Serial_Number() == false) {
numberEnoughFlag = false;
}
dataBuffer[127] = myPort.read();
}
//println(dataBuffer);
}
number_got_of_img_now += 127;
print('.');
if (number_got_of_img_now <= pic_length) { //havn't got all the data
for (int i = 0; i < 127; ++i) {
try {
output.write(dataBuffer[i]);
}
catch (IOException e) {
e.printStackTrace();
}
}
myPort.write('1');
}else { //finished the communicating,
for (int i = 0; i < (127 + pic_length - number_got_of_img_now); ++i) {
try {
output.write(dataBuffer[i]);
}
catch (IOException e) {
e.printStackTrace();
}
}
try {
output.flush(); // Writes the remaining data to the file
output.close(); // Finishes the file
}
catch (IOException e) {
e.printStackTrace();
}
img = loadImage("pic.jpg"); //show the image
image(img, 0, 0);
system_state = 0;
myPort.write('1');
break;
}
// try { //when get the data with wire, so there is no need of checking the communication.
// output.write(myPort.read());
// }
// catch (IOException e) {
// e.printStackTrace();
// }
// number_got_of_img_now++;
// if (number_got_of_img_now == pic_length) {
// try {
// output.flush(); // Writes the remaining data to the file
// output.close(); // Finishes the file
// }
// catch (IOException e) {
// e.printStackTrace();
// }
// img = loadImage("pic.jpg");
// image(img, 0, 0);
// system_state = 0;
// break;
// }
}
}
}else {
while (myPort.available() > 0) { //system_state = 0, just print the data from serial
print(myPort.readChar());
}
}
}
/**
* [Get_Valid_Serial_Number description] return false if there is still nothing from Serial after 100ms. or return true. in case of some bytes lost while communication,
* but the program is waiting all the time.
*/
boolean Get_Valid_Serial_Number(){
int waitingCount = 0;
while (myPort.available() <= 0) {
delay(1);
if (waitingCount++ > 100) {
return false;
}
}
return true;
}
void keyPressed() {
if(key == 'a'){ //press a to get an image
output = createOutput("pic.jpg");
myPort.write('2');
is_first_change_to_system_state_1 = 0;
number_got_of_img_now = 0;
system_state = 1;
delay(5000);
while (myPort.available() > 0 ) {
print(myPort.readChar());
}
myPort.write('2');
println("taking a photo");
}
if (key == '1') { //press 1 to pull up the solar battery
myPort.write('2');
delay(2000);
while (myPort.available() > 0 ) {
print(myPort.readChar());
}
myPort.write('3');
println("taking up servo");
}
if (key == '2') { //press 2 to pull down the solar battery.
myPort.write('2');
delay(2000);
while (myPort.available() > 0 ) {
print(myPort.readChar());
}
myPort.write('4');
println("taking down servo");
}
if (key == 'r'){ //press r to return prograss of arduino, press it when you restart the program
//but Arduino is still trying to finish sending the jpg file data.
myPort.write('3');
system_state = 0;
}
}
tip
如果你不是想做一个完全一样的小卫星的话,我觉得里面可以用的部分主要所有这些:
- 带校验的无线串口传输,大量数据比如图片什么的在无线传输的时候回丢包什么的,所以加一个校验是很有必要的。
- 通过串口传输图片,你懂的串口并不是很适合传输图片,即使是有线串口,大概也要几秒钟的样子,但是如果你不得不这样的话这里面的代码可以让你做到这样的事情。。