sync
This commit is contained in:
parent
eb7b4a3060
commit
8521834143
|
@ -1,4 +1,5 @@
|
|||
*~
|
||||
*.pyc
|
||||
build
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
|
|
|
@ -4,12 +4,34 @@ Lots of help from here:
|
|||
http://stackoverflow.com/questions/1093598/pyserial-how-to-read-last-line-sent-from-serial-device
|
||||
"""
|
||||
from threading import Thread
|
||||
|
||||
import time
|
||||
import serial
|
||||
import struct
|
||||
|
||||
last_received = ''
|
||||
profile = []
|
||||
|
||||
status = [0, 23, 23, 0, 0, 0]
|
||||
|
||||
oven_connected = False
|
||||
|
||||
profile = [
|
||||
150,
|
||||
200,
|
||||
217,
|
||||
260,
|
||||
480,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
2,
|
||||
-1,
|
||||
-6,
|
||||
60,
|
||||
180,
|
||||
60,
|
||||
150,
|
||||
20,
|
||||
40]
|
||||
|
||||
PI_TS_MIN = 0
|
||||
PI_TS_MAX = 1
|
||||
|
@ -17,8 +39,10 @@ PI_TL = 2
|
|||
PI_TP = 3
|
||||
PI_TIME_MAX = 4
|
||||
|
||||
PI_RAMP_UP_MIN = 5
|
||||
PI_RAMP_UP_MAX = 6
|
||||
PI_TS_RAMP_UP_MIN = 5
|
||||
PI_TS_RAMP_UP_MAX = 6
|
||||
PI_TP_RAMP_UP_MIN = 5
|
||||
PI_TP_RAMP_UP_MAX = 6
|
||||
PI_RAMP_DOWN_MIN = 7
|
||||
PI_RAMP_DOWN_MAX = 8
|
||||
|
||||
|
@ -29,19 +53,29 @@ PI_TL_DURATION_MAX = 12
|
|||
PI_TP_DURATION_MIN = 13
|
||||
PI_TP_DURATION_MAX = 14
|
||||
|
||||
def recv_config(ser):
|
||||
global profile
|
||||
ser.write(chr(255))
|
||||
ser.flush()
|
||||
t = ser.read(30)
|
||||
profile = struct.unpack("hhhhhhhhhhhhhhhhh", t)
|
||||
ser.flushInput()
|
||||
|
||||
|
||||
def receiving(ser):
|
||||
global last_received
|
||||
buffer = ''
|
||||
ser.write(chr(255))
|
||||
global status
|
||||
try:
|
||||
time.sleep(2)
|
||||
recv_config(ser)
|
||||
except Exception, e:
|
||||
print e
|
||||
pass
|
||||
|
||||
while 1:
|
||||
ser.write(chr(254))
|
||||
ser.flush()
|
||||
profile = struct.unpack("hhhhhhhhhhhhhhh", ser.read(30))
|
||||
status = struct.unpack("hhhhhb", ser.read(11))
|
||||
ser.flushInput()
|
||||
while 1:
|
||||
ser.write(chr(254))
|
||||
ser.flush()
|
||||
last_received = ser.read(11)
|
||||
print repr(last_received)
|
||||
ser.flushInput()
|
||||
|
||||
|
||||
class SerialData(object):
|
||||
|
@ -57,15 +91,31 @@ class SerialData(object):
|
|||
Thread(target=receiving, args=(self.ser,)).start()
|
||||
|
||||
def next(self):
|
||||
global status
|
||||
if not self.ser:
|
||||
return 100 #return anything so we can test when Arduino isn't connected
|
||||
return status[1]
|
||||
|
||||
try:
|
||||
return int(struct.unpack("hhhhhb", last_received)[1])
|
||||
except Exception, e:
|
||||
print e
|
||||
return 0
|
||||
return status[1]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def send_config(self):
|
||||
if not self.ser:
|
||||
return
|
||||
|
||||
global profile
|
||||
self.ser.write(struct.pack("hhhhhhhhhhhhhhhhh", profile))
|
||||
|
||||
def send_start(self):
|
||||
if not self.ser:
|
||||
return False
|
||||
|
||||
self.ser.write(chr(251))
|
||||
return True
|
||||
|
||||
def connected(self):
|
||||
return self.ser != None
|
||||
|
||||
def __del__(self):
|
||||
if self.ser:
|
||||
|
|
|
@ -16,8 +16,14 @@ set(ARDUINO_DEFAULT_BOARD atmega328) # Default Board ID, when not specified
|
|||
set(ARDUINO_DEFAULT_PORT /dev/ttyUSB0) # Default Port, when not specified
|
||||
|
||||
link_directories(/usr/share/arduino/libraries)
|
||||
#
|
||||
# generate_arduino_library(reflowctl_lib
|
||||
# SRCS oven_control.cpp profile.cpp
|
||||
# HDRS oven_control.h profile.h
|
||||
# BOARD atmega328)
|
||||
|
||||
generate_arduino_firmware(reflowctl
|
||||
SKETCH reflowctl
|
||||
SRCS oven_control.cpp profile.cpp main.cpp
|
||||
HDRS oven_control.h profile.h
|
||||
PORT /dev/ttyUSB0
|
||||
BOARD atmega328)
|
||||
|
|
|
@ -1,456 +0,0 @@
|
|||
#include "oven_control.h"
|
||||
#include <DFR_Key.h>
|
||||
#include <LiquidCrystal.h>
|
||||
#include "profile.h"
|
||||
|
||||
//Pin assignments for SainSmart LCD Keypad Shield
|
||||
LiquidCrystal _lcd(8, 9, 4, 5, 6, 7);
|
||||
DFR_Key _keypad;
|
||||
Profile _profile;
|
||||
|
||||
OvenCtl::OvenCtl() {
|
||||
|
||||
time = 0;
|
||||
temperature = 1;
|
||||
last_temperature = 1;
|
||||
actual_dt = 0;
|
||||
// timestamps of event beginnings/ends
|
||||
Ts_time_start = 0;
|
||||
Ts_time_end = 0;
|
||||
Tl_time_start = 0;
|
||||
Tl_time_end = 0;
|
||||
Tp_time_start = 0;
|
||||
Tp_time_end = 0;
|
||||
|
||||
// thermostat
|
||||
set_min = 0;
|
||||
set_max = 0;
|
||||
set_dt_min = 0;
|
||||
set_dt_max = 0;
|
||||
|
||||
state = 0;
|
||||
error_condition = 0;
|
||||
is_oven_heating = false;
|
||||
|
||||
// ui stuff
|
||||
led_on = false;
|
||||
disable_checks = false;
|
||||
lcd = &_lcd;
|
||||
keypad = &_keypad;
|
||||
profile = &_profile;
|
||||
lcd->begin(16, 2);
|
||||
}
|
||||
|
||||
void OvenCtl::reset() {
|
||||
digitalWrite(7, LOW);
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::send_state() {
|
||||
Serial.write(time & 0xff);
|
||||
Serial.write((time>>8) & 0xff);
|
||||
Serial.write(temperature & 0xff);
|
||||
Serial.write((temperature >> 8) & 0xff);
|
||||
Serial.write(last_temperature & 0xff);
|
||||
Serial.write((last_temperature >> 8) & 0xff);
|
||||
Serial.write(state & 0xff);
|
||||
Serial.write((state >> 8) & 0xff);
|
||||
Serial.write(error_condition & 0xff);
|
||||
Serial.write((error_condition >> 8) & 0xff);
|
||||
Serial.write(is_oven_heating);
|
||||
Serial.flush();
|
||||
}
|
||||
|
||||
void OvenCtl::send_config() {
|
||||
int tmp;
|
||||
for (int i=0;i < PI_END; i++)
|
||||
{
|
||||
tmp = profile->data[i];
|
||||
Serial.write(tmp & 0xff);
|
||||
Serial.write((tmp >> 8 ) & 0xff);
|
||||
}
|
||||
Serial.flush();
|
||||
}
|
||||
|
||||
void OvenCtl::dispatch_input_config(int cmd) {
|
||||
if (cmd == 255)
|
||||
send_config();
|
||||
else if (cmd == 254)
|
||||
recv_config();
|
||||
else if (cmd == 250)
|
||||
reset();
|
||||
else if (cmd == 253)
|
||||
;
|
||||
}
|
||||
|
||||
void OvenCtl::recv_config() {
|
||||
|
||||
}
|
||||
|
||||
void OvenCtl::handle_states() {
|
||||
int cmd = -1;
|
||||
|
||||
if (state > 0)
|
||||
{
|
||||
time++;
|
||||
get_temp();
|
||||
check_dt();
|
||||
}
|
||||
|
||||
if (error_condition != 0) {
|
||||
set_error_state();
|
||||
}
|
||||
|
||||
if (Serial.available() > 0) {
|
||||
cmd = Serial.read();
|
||||
if (cmd == 255)
|
||||
send_config();
|
||||
else if (cmd == 254)
|
||||
send_state();
|
||||
else if (cmd == 253)
|
||||
recv_config();
|
||||
else if (cmd == 252)
|
||||
reset();
|
||||
else if (cmd == 251)
|
||||
set_start_state();
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case CONFIG_STATE:
|
||||
if (profile->handle_config_state(lcd, keypad))
|
||||
set_start_state();
|
||||
break;
|
||||
case START_STATE:
|
||||
handle_start_state();
|
||||
break;
|
||||
case PREHEAT_STATE:
|
||||
handle_preheat_state();
|
||||
break;
|
||||
case RAMP_UP_STATE:
|
||||
handle_ramp_up_state();
|
||||
break;
|
||||
case TAL_FIRST_STATE:
|
||||
handle_tal_first_state();
|
||||
break;
|
||||
case PEAK_STATE:
|
||||
handle_peak_state();
|
||||
break;
|
||||
case TAL_SECOND_STATE:
|
||||
Tl_time_end = time;
|
||||
handle_tal_second_state();
|
||||
break;
|
||||
case RAMP_DOWN_STATE:
|
||||
handle_ramp_down_state();
|
||||
break;
|
||||
case END_STATE:
|
||||
handle_end_state();
|
||||
break;
|
||||
case ERROR_STATE:
|
||||
handle_error_state();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
control_oven();
|
||||
if (state > 0) {
|
||||
print_status();
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void OvenCtl::print_status() {
|
||||
if (error_condition == 0) {
|
||||
String tmp("T: ");
|
||||
if (time < 10)
|
||||
tmp += "00";
|
||||
else if (time < 100)
|
||||
tmp += "0";
|
||||
tmp += time;
|
||||
tmp += " Tmp: ";
|
||||
if (temperature < 10)
|
||||
tmp += "00";
|
||||
else if (temperature < 100)
|
||||
tmp += "0";
|
||||
tmp += temperature;
|
||||
lcd->setCursor(0, 0);
|
||||
lcd->print(tmp);
|
||||
|
||||
tmp = "Profile: ";
|
||||
tmp += state;
|
||||
tmp += "/";
|
||||
tmp += END_STATE;
|
||||
lcd->setCursor(0, 1);
|
||||
lcd->print(tmp);
|
||||
lcd->setCursor(13, 1);
|
||||
if (is_oven_heating)
|
||||
lcd->print("on ");
|
||||
else
|
||||
lcd->print("off");
|
||||
}
|
||||
else {
|
||||
lcd->clear();
|
||||
lcd->print("Error:");
|
||||
lcd->setCursor(0, 1);
|
||||
if (error_condition & E_DT_MIN)
|
||||
lcd->print("K/s too low");
|
||||
if (error_condition & E_DT_MAX)
|
||||
lcd->print("K/s too high");
|
||||
if (error_condition & E_TIME_MAX)
|
||||
lcd->print("reflow too long");
|
||||
if (error_condition & E_TS_TOO_SHORT)
|
||||
lcd->print("ts too short");
|
||||
if (error_condition & E_TS_TOO_LONG)
|
||||
lcd->print("ts too long");
|
||||
if (error_condition & E_TL_TOO_SHORT)
|
||||
lcd->print("tal too short");
|
||||
if (error_condition & E_TL_TOO_LONG)
|
||||
lcd->print("tal too long");
|
||||
if (error_condition & E_TP_TOO_LONG)
|
||||
lcd->print("peak too short");
|
||||
if (error_condition & E_TP_TOO_SHORT)
|
||||
lcd->print("peak too long");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::control_oven() {
|
||||
if (temperature < set_min && !is_oven_heating) {
|
||||
is_oven_heating = true;
|
||||
// Serial.println("Oven turned on");
|
||||
}
|
||||
else if (temperature > set_min && is_oven_heating) {
|
||||
is_oven_heating = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::set_temp(int min, int max, int dt_min, int dt_max) {
|
||||
set_min = min;
|
||||
set_max = max;
|
||||
set_dt_min = dt_min;
|
||||
set_dt_max = dt_max;
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::get_temp() {
|
||||
last_temperature = temperature;
|
||||
temperature = int(float(analogRead(2)) * 0.2929);
|
||||
actual_dt = temperature - last_temperature;
|
||||
}
|
||||
|
||||
void OvenCtl::check_dt() {
|
||||
if (disable_checks)
|
||||
return;
|
||||
if (actual_dt > set_dt_max) {
|
||||
error_condition |= E_DT_MAX;
|
||||
}
|
||||
if (actual_dt < set_dt_min) {
|
||||
error_condition |= E_DT_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
void OvenCtl::check_max_duration() {
|
||||
if (disable_checks)
|
||||
return;
|
||||
if (time > profile->data[PI_TIME_MAX]) {
|
||||
error_condition |= E_TIME_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
void OvenCtl::check_Ts_duration_min() {
|
||||
if (disable_checks)
|
||||
return;
|
||||
Tl_time_end = time;
|
||||
if (time - Tl_time_start < profile->data[PI_TL_DURATION_MIN]) {
|
||||
error_condition |= E_TL_TOO_SHORT;
|
||||
}
|
||||
}
|
||||
|
||||
void OvenCtl::check_Ts_duration_max() {
|
||||
if (disable_checks)
|
||||
return;
|
||||
if (time - Ts_time_start > profile->data[PI_TL_DURATION_MAX]) {
|
||||
error_condition |= E_TS_TOO_LONG;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::check_Tl_duration_min() {
|
||||
if (disable_checks)
|
||||
return;
|
||||
Tl_time_end = time;
|
||||
if (time - Tl_time_start < profile->data[PI_TL_DURATION_MIN]) {
|
||||
error_condition |= E_TL_TOO_SHORT;
|
||||
}
|
||||
}
|
||||
|
||||
void OvenCtl::check_Tl_duration_max() {
|
||||
if (disable_checks)
|
||||
return;
|
||||
if (time - Tl_time_start > profile->data[PI_TL_DURATION_MAX]) {
|
||||
error_condition |= E_TL_TOO_LONG;
|
||||
}
|
||||
}
|
||||
|
||||
void OvenCtl::check_Tp_duration_min() {
|
||||
Tp_time_end = time;
|
||||
if (time - Tp_time_start < profile->data[PI_TP_DURATION_MIN]) {
|
||||
error_condition |= E_TP_TOO_SHORT;
|
||||
}
|
||||
}
|
||||
|
||||
void OvenCtl::check_Tp_duration_max() {
|
||||
if (disable_checks)
|
||||
return;
|
||||
if (time - Tp_time_start > profile->data[PI_TP_DURATION_MAX]) {
|
||||
error_condition |= E_TP_TOO_LONG;
|
||||
}
|
||||
}
|
||||
|
||||
void OvenCtl::set_config_state() {
|
||||
profile->print_config_state_0(lcd);
|
||||
}
|
||||
|
||||
void OvenCtl::set_start_state() {
|
||||
led_on = false;
|
||||
digitalWrite(13, LOW);
|
||||
error_condition = 0;
|
||||
state = START_STATE;
|
||||
get_temp();
|
||||
last_temperature = temperature;
|
||||
actual_dt = temperature - last_temperature;
|
||||
set_temp(profile->data[PI_TP]-5, profile->data[PI_TP], 0, profile->data[PI_RAMP_UP_MAX]);
|
||||
lcd->clear();
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::set_preheat_state() {
|
||||
// Serial.println("Changing state to PREHEAT_STATE");
|
||||
state++;
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::set_ramp_up_state() {
|
||||
state++;
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::set_tal_first_state() {
|
||||
state++;
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::set_peak_state() {
|
||||
state++;
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::set_tal_second_state() {
|
||||
set_temp(0, 25, -3, -6);
|
||||
state++;
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::set_ramp_down_state() {
|
||||
state++;
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::set_end_state() {
|
||||
state++;
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::set_error_state() {
|
||||
if (state != ERROR_STATE) {
|
||||
set_temp(0, 0, 0, 0);
|
||||
state = ERROR_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::handle_config_state() {
|
||||
if (profile->handle_config_state(lcd, keypad))
|
||||
state++;
|
||||
}
|
||||
|
||||
void OvenCtl::handle_start_state() {
|
||||
check_max_duration();
|
||||
if (temperature > profile->data[PI_TS_MIN]) {
|
||||
Ts_time_start = time;
|
||||
set_preheat_state();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::handle_preheat_state() {
|
||||
check_Ts_duration_max();
|
||||
check_max_duration();
|
||||
if (temperature > profile->data[PI_TS_MAX]) {
|
||||
check_Ts_duration_min();
|
||||
set_ramp_up_state();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::handle_ramp_up_state() {
|
||||
check_max_duration();
|
||||
if (temperature > profile->data[PI_TL]) {
|
||||
Tl_time_start = time;
|
||||
set_tal_first_state();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::handle_tal_first_state() {
|
||||
check_max_duration();
|
||||
check_Tl_duration_max();
|
||||
if (temperature > profile->data[PI_TP] - 5) {
|
||||
Tp_time_start = time;
|
||||
set_peak_state();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::handle_peak_state() {
|
||||
check_Tl_duration_max();
|
||||
check_Tp_duration_max();
|
||||
if (time - Tp_time_start > profile->data[PI_TP_DURATION_MAX]) {
|
||||
check_Tp_duration_min();
|
||||
set_tal_second_state();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::handle_tal_second_state() {
|
||||
check_Tl_duration_max();
|
||||
if (temperature < profile->data[PI_TL]) {
|
||||
check_Tl_duration_min();
|
||||
set_ramp_down_state();
|
||||
}
|
||||
}
|
||||
|
||||
void OvenCtl::handle_ramp_down_state() {
|
||||
if (temperature < profile->data[PI_TS_MIN]) {
|
||||
set_end_state();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::handle_end_state() {
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::handle_error_state() {
|
||||
if (led_on) {
|
||||
digitalWrite(13, LOW);
|
||||
led_on = false;
|
||||
}
|
||||
else {
|
||||
digitalWrite(13, HIGH);
|
||||
led_on = true;
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
#ifndef _H_OVEN_CTL
|
||||
#define _H_OVEN_CTL
|
||||
|
||||
//
|
||||
//
|
||||
// states
|
||||
#define CONFIG_STATE 0
|
||||
#define START_STATE 1
|
||||
#define PREHEAT_STATE 2
|
||||
#define RAMP_UP_STATE 3
|
||||
#define TAL_FIRST_STATE 4
|
||||
#define PEAK_STATE 5
|
||||
#define TAL_SECOND_STATE 6
|
||||
#define RAMP_DOWN_STATE 7
|
||||
#define END_STATE 8
|
||||
#define ERROR_STATE 9
|
||||
|
||||
// error conditions
|
||||
#define E_DT_MIN 1 // temperature dt too small
|
||||
#define E_DT_MAX 2 // temperature dt too big
|
||||
#define E_TIME_MAX 4 // reflow process does take too long
|
||||
#define E_TS_TOO_SHORT 8 // Ts duration too short
|
||||
#define E_TS_TOO_LONG 16 // Ts duration too long
|
||||
#define E_TL_TOO_SHORT 32 // Tl duration too short
|
||||
#define E_TL_TOO_LONG 64 // Tl duration too long
|
||||
#define E_TP_TOO_SHORT 128 // Tp duration too short
|
||||
#define E_TP_TOO_LONG 256 // Tp duration too long
|
||||
#define E_CONFIG 512 // error happened in config state
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class LiquidCrystal;
|
||||
class DFR_Key;
|
||||
class Profile;
|
||||
|
||||
class OvenCtl {
|
||||
public:
|
||||
|
||||
OvenCtl();
|
||||
void handle_states();
|
||||
void set_config_state();
|
||||
|
||||
private:
|
||||
// system time, timestamps and temperatures from sensors
|
||||
int time; // profile seconds
|
||||
int temperature; // actual oven temp
|
||||
int last_temperature; // last oven temp
|
||||
int actual_dt; // actual difference from last to actual temperatur
|
||||
|
||||
// timestamps of event beginnings/ends
|
||||
int Ts_time_start;
|
||||
int Ts_time_end;
|
||||
int Tl_time_start;
|
||||
int Tl_time_end;
|
||||
int Tp_time_start;
|
||||
int Tp_time_end;
|
||||
|
||||
// thermostat
|
||||
int set_min;
|
||||
int set_max;
|
||||
int set_dt_min;
|
||||
int set_dt_max;
|
||||
|
||||
// ui stuff
|
||||
boolean led_on;
|
||||
boolean disable_checks;
|
||||
|
||||
// state machine
|
||||
unsigned int error_condition;
|
||||
unsigned int state;
|
||||
boolean is_oven_heating;
|
||||
|
||||
LiquidCrystal * lcd;
|
||||
DFR_Key * keypad;
|
||||
Profile * profile;
|
||||
|
||||
void print_status();
|
||||
void control_oven();
|
||||
void set_temp(int, int, int, int);
|
||||
void get_temp();
|
||||
void check_dt();
|
||||
void check_max_duration();
|
||||
|
||||
void set_start_state();
|
||||
void set_preheat_state();
|
||||
void set_tal_first_state();
|
||||
void set_ramp_up_state();
|
||||
void set_peak_state();
|
||||
void set_tal_second_state();
|
||||
void set_ramp_down_state();
|
||||
void set_end_state();
|
||||
void set_error_state();
|
||||
|
||||
void handle_config_state();
|
||||
void handle_start_state();
|
||||
void handle_ramp_up_state();
|
||||
void handle_preheat_state();
|
||||
void handle_tal_first_state();
|
||||
void handle_peak_state();
|
||||
void handle_tal_second_state();
|
||||
void handle_ramp_down_state();
|
||||
void handle_end_state();
|
||||
void handle_error_state();
|
||||
|
||||
void check_Ts_duration_min();
|
||||
void check_Ts_duration_max();
|
||||
void check_Tl_duration_min();
|
||||
void check_Tl_duration_max();
|
||||
void check_Tp_duration_min();
|
||||
void check_Tp_duration_max();
|
||||
|
||||
void send_state();
|
||||
void send_config();
|
||||
void reset();
|
||||
|
||||
void recv_config();
|
||||
|
||||
void dispatch_input_config(int);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
191
libs/profile.cpp
191
libs/profile.cpp
|
@ -1,191 +0,0 @@
|
|||
#include "profile.h"
|
||||
#include "oven_control.h"
|
||||
#include <LiquidCrystal.h>
|
||||
#include <DFR_Key.h>
|
||||
|
||||
#define P_TS_MIN 0
|
||||
#define P_TS_MAX 1
|
||||
#define P_TL 2
|
||||
#define P_TP 3
|
||||
#define P_TIME_MAX 4
|
||||
|
||||
// PROFILE TEMP PER SECOND RATES
|
||||
#define P_RAMP_UP_RATE_MIN 5
|
||||
#define P_RAMP_UP_RATE_MAX 6
|
||||
#define P_RAMP_DOWN_MAX 7
|
||||
#define P_RAMP_DOWN_MIN 8
|
||||
|
||||
// PROFILE TEMP DURATIONS
|
||||
#define P_TS_DURATION_MIN 9
|
||||
#define P_TS_DURATION_MAX 10
|
||||
#define P_TL_DURATION_MIN 11
|
||||
#define P_TL_DURATION_MAX 12
|
||||
#define P_TP_DURATION_MIN 13
|
||||
#define P_TP_DURATION_MAX 14
|
||||
#define P_END 15
|
||||
|
||||
|
||||
Profile::Profile() :
|
||||
data({150, // °C
|
||||
200, // °C
|
||||
217, // °C
|
||||
260, // 245-260°C
|
||||
480, // seconds
|
||||
|
||||
// profile temp per second rates
|
||||
0, // not used yet
|
||||
50, // 3°C/second
|
||||
-2, // 2°C/seconds min
|
||||
-6, // 6°C/seconds max
|
||||
|
||||
// profile temp durations
|
||||
60,
|
||||
180,
|
||||
60,
|
||||
150,
|
||||
20,
|
||||
40}),
|
||||
config_index(0),
|
||||
config_state(0),
|
||||
key(NO_KEY) {}
|
||||
|
||||
boolean Profile::handle_config_state(LiquidCrystal * lcd, DFR_Key * keypad) {
|
||||
boolean changed = false;
|
||||
|
||||
key = keypad->getKey();
|
||||
|
||||
switch (config_state) {
|
||||
case 0:
|
||||
if (key == SELECT_KEY) {
|
||||
config_state = 2;
|
||||
}
|
||||
else if (key > 0) {
|
||||
config_state++;
|
||||
print_config_state(lcd);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (key) {
|
||||
case LEFT_KEY:
|
||||
config_index = (config_index-1) % P_END;
|
||||
changed = true;
|
||||
break;
|
||||
case RIGHT_KEY:
|
||||
config_index = (config_index+1) % P_END;
|
||||
changed = true;
|
||||
break;
|
||||
case UP_KEY:
|
||||
data[config_index]++;
|
||||
changed = true;
|
||||
break;
|
||||
case DOWN_KEY:
|
||||
data[config_index]--;
|
||||
changed = true;
|
||||
break;
|
||||
case SELECT_KEY:
|
||||
config_state = 2;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
if (changed)
|
||||
print_config_state(lcd);
|
||||
break;
|
||||
case 2:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Profile::print_config_state_0(LiquidCrystal * lcd) {
|
||||
lcd->clear();
|
||||
lcd->setCursor(0, 0);
|
||||
lcd->print("start | config");
|
||||
lcd->setCursor(0, 1);
|
||||
lcd->print("[sel] | [other]");
|
||||
}
|
||||
|
||||
void Profile::print_config_state(LiquidCrystal * lcd) {
|
||||
lcd->clear();
|
||||
switch (config_index) {
|
||||
case P_TS_MIN:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("P_TS_MIN: ");
|
||||
lcd->print(data[PI_TS_MIN]);
|
||||
break;
|
||||
case P_TS_MAX:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("P_TS_MAX: ");
|
||||
lcd->print(data[PI_TS_MAX]);
|
||||
break;
|
||||
case P_TL:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("Tl: ");
|
||||
lcd->print(data[PI_TL]);
|
||||
break;
|
||||
case P_TP:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("Tp: ");
|
||||
lcd->print(data[PI_TP]);
|
||||
break;
|
||||
case P_TIME_MAX:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("time_max: ");
|
||||
lcd->print(data[PI_TIME_MAX]);
|
||||
break;
|
||||
|
||||
// PROFILE TEMP PER SECOND RATES
|
||||
case P_RAMP_UP_RATE_MIN:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("ramp_up_min: ");
|
||||
lcd->print(data[PI_RAMP_UP_MIN]);
|
||||
break;
|
||||
case P_RAMP_UP_RATE_MAX:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("ramp_up_max: ");
|
||||
lcd->print(data[PI_RAMP_UP_MAX]);
|
||||
break;
|
||||
case P_RAMP_DOWN_MAX:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("ramp_down_min: ");
|
||||
lcd->print(data[PI_RAMP_DOWN_MIN]);
|
||||
break;
|
||||
case P_RAMP_DOWN_MIN:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("ramp_down_max: ");
|
||||
lcd->print(data[PI_RAMP_DOWN_MAX]);
|
||||
break;
|
||||
|
||||
// PROFILE TEMP DURATIONS
|
||||
case P_TS_DURATION_MIN:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("Ts_duration_min: ");
|
||||
lcd->print(data[PI_TS_DURATION_MIN]);
|
||||
break;
|
||||
case P_TS_DURATION_MAX:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("Ts_duration_max: ");
|
||||
lcd->print(data[PI_TS_DURATION_MAX]);
|
||||
break;
|
||||
case P_TL_DURATION_MIN:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("Tl_duration_min: ");
|
||||
lcd->print(data[PI_TL_DURATION_MIN]);
|
||||
break;
|
||||
case P_TL_DURATION_MAX:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("Tl_duration_max: ");
|
||||
lcd->print(data[PI_TL_DURATION_MAX]);
|
||||
break;
|
||||
case P_TP_DURATION_MIN:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("Tp_duration_min: ");
|
||||
lcd->print(data[PI_TP_DURATION_MIN]);
|
||||
break;
|
||||
case P_TP_DURATION_MAX:
|
||||
lcd->setCursor(0,0);
|
||||
lcd->print("Tp_duration_max: ");
|
||||
lcd->print(data[PI_TP_DURATION_MAX]);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
#ifndef _H_PROFILE
|
||||
#define _H_PROFILE
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class LiquidCrystal;
|
||||
class DFR_Key;
|
||||
|
||||
#define PI_TS_MIN 0
|
||||
#define PI_TS_MAX 1
|
||||
#define PI_TL 2
|
||||
#define PI_TP 3
|
||||
#define PI_TIME_MAX 4
|
||||
|
||||
// profile temp per second rates
|
||||
#define PI_RAMP_UP_MIN 5
|
||||
#define PI_RAMP_UP_MAX 6
|
||||
#define PI_RAMP_DOWN_MIN 7
|
||||
#define PI_RAMP_DOWN_MAX 8
|
||||
|
||||
// profile temp durations
|
||||
#define PI_TS_DURATION_MIN 9
|
||||
#define PI_TS_DURATION_MAX 10
|
||||
#define PI_TL_DURATION_MIN 11
|
||||
#define PI_TL_DURATION_MAX 12
|
||||
#define PI_TP_DURATION_MIN 13
|
||||
#define PI_TP_DURATION_MAX 14
|
||||
#define PI_END 15
|
||||
|
||||
|
||||
class Profile {
|
||||
public:
|
||||
int data[15];
|
||||
|
||||
unsigned int config_index;
|
||||
int config_state;
|
||||
int key;
|
||||
|
||||
Profile();
|
||||
boolean handle_config_state(LiquidCrystal * lcd, DFR_Key * keypad);
|
||||
void print_config_state(LiquidCrystal * lcd);
|
||||
void print_config_state_0(LiquidCrystal * lcd);
|
||||
};
|
||||
|
||||
#endif
|
35
libs/ui.h
35
libs/ui.h
|
@ -1,35 +0,0 @@
|
|||
// #ifndef _H_UI
|
||||
// #define _H_UI
|
||||
//
|
||||
// #include <LiquidCrystal.h>
|
||||
// #include <DFR_Key.h>
|
||||
//
|
||||
// class Profile {
|
||||
// public:
|
||||
// int Ts_min;
|
||||
// int Ts_max;
|
||||
// int Tl;
|
||||
// int Tp;
|
||||
// int time_max;
|
||||
//
|
||||
// // profile temp per second rates
|
||||
// int ramp_up_min;
|
||||
// int ramp_up_max;
|
||||
// int ramp_down_max;
|
||||
// int ramp_down_min;
|
||||
//
|
||||
// // profile temp durations
|
||||
// int Ts_duration_min;
|
||||
// int Ts_duration_max;
|
||||
// int Tl_duration_min;
|
||||
// int Tl_duration_max;
|
||||
// int Tp_duration_min;
|
||||
// int Tp_duration_max;
|
||||
// int config_index;
|
||||
//
|
||||
// Profile();
|
||||
// void handle_config_state(LiquidCrystal & lcd, DFR_Key & keypad);
|
||||
// void print_config_state(LiquidCrystal & lcd);
|
||||
// };
|
||||
//
|
||||
// #endif
|
|
@ -0,0 +1,251 @@
|
|||
#include "oven_control.h"
|
||||
#include <DFR_Key.h>
|
||||
#include <LiquidCrystal.h>
|
||||
#include "profile.h"
|
||||
|
||||
//Pin assignments for SainSmart LCD Keypad Shield
|
||||
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
|
||||
DFR_Key keypad;
|
||||
Profile profile;
|
||||
|
||||
OvenCtl::OvenCtl() {
|
||||
|
||||
time = 0;
|
||||
temperature = 1;
|
||||
last_temperature = 1;
|
||||
actual_dt = 0.f;
|
||||
|
||||
|
||||
// timestamps of event beginnings/ends
|
||||
Ts_time_start = 0;
|
||||
Ts_time_end = 0;
|
||||
Tl_time_start = 0;
|
||||
Tl_time_end = 0;
|
||||
Tp_time_start = 0;
|
||||
Tp_time_end = 0;
|
||||
|
||||
// thermostat
|
||||
set_min = 0;
|
||||
set_max = 0;
|
||||
set_dt_min = 0;
|
||||
set_dt_max = 0;
|
||||
|
||||
op_state = OP_CONFIG;
|
||||
profile_state = START_STATE;
|
||||
error_condition = 0;
|
||||
is_oven_heating = false;
|
||||
|
||||
actual_hysteresis = ramp_up_hysteresis = 1.0; // s
|
||||
ramp_down_hysteresis = 1.0; // s
|
||||
|
||||
// ui stuff
|
||||
disable_checks = false;
|
||||
lcd.begin(16, 2);
|
||||
}
|
||||
|
||||
void OvenCtl::reset() {
|
||||
digitalWrite(7, LOW);
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::send_state() {
|
||||
Serial.write(time & 0xff);
|
||||
Serial.write((time>>8) & 0xff);
|
||||
Serial.write(temperature & 0xff);
|
||||
Serial.write((temperature >> 8) & 0xff);
|
||||
Serial.write(last_temperature & 0xff);
|
||||
Serial.write((last_temperature >> 8) & 0xff);
|
||||
Serial.write(profile_state & 0xff);
|
||||
Serial.write((profile_state >> 8) & 0xff);
|
||||
Serial.write(error_condition & 0xff);
|
||||
Serial.write((error_condition >> 8) & 0xff);
|
||||
Serial.write(is_oven_heating);
|
||||
Serial.flush();
|
||||
}
|
||||
|
||||
void OvenCtl::send_config() {
|
||||
int tmp;
|
||||
for (int i=0;i < PI_END; i++)
|
||||
{
|
||||
tmp = profile.data[i];
|
||||
Serial.write(tmp & 0xff);
|
||||
Serial.write((tmp >> 8 ) & 0xff);
|
||||
}
|
||||
Serial.flush();
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::recv_config() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::dispatch_input_config(int cmd) {
|
||||
if (cmd == 255)
|
||||
send_config();
|
||||
else if (cmd == 254)
|
||||
recv_config();
|
||||
else if (cmd == 250)
|
||||
reset();
|
||||
else if (cmd == 253)
|
||||
;
|
||||
}
|
||||
|
||||
void OvenCtl::handle_stand_alone_state() {
|
||||
time++;
|
||||
get_temp();
|
||||
check_dt();
|
||||
}
|
||||
|
||||
void OvenCtl::handle_remote_state() {
|
||||
time++;
|
||||
get_temp();
|
||||
check_dt();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* handles input, dispatching state dependend modes to state handlers
|
||||
*
|
||||
*
|
||||
*/
|
||||
void OvenCtl::loop() {
|
||||
int cmd = -1;
|
||||
|
||||
switch (op_state) {
|
||||
case OP_CONFIG:
|
||||
// if (profile.handle_config_state())
|
||||
// set_start_state();
|
||||
break;
|
||||
case OP_STAND_ALONE:
|
||||
// if (profile.handle_config_state())
|
||||
// set_start_state();
|
||||
// break;
|
||||
case OP_REMOTE:
|
||||
// if (profile.handle_config_state())
|
||||
// set_start_state();
|
||||
break;
|
||||
}
|
||||
|
||||
// if (error_condition != 0) {
|
||||
// set_error_state();
|
||||
// }
|
||||
|
||||
if (Serial.available() > 0) {
|
||||
cmd = Serial.read();
|
||||
if (cmd == 255)
|
||||
send_config();
|
||||
else if (cmd == 254)
|
||||
send_state();
|
||||
else if (cmd == 253)
|
||||
recv_config();
|
||||
else if (cmd == 252)
|
||||
reset();
|
||||
// else if (cmd == 251)
|
||||
// set_start_state();
|
||||
}
|
||||
|
||||
|
||||
|
||||
control_oven();
|
||||
if (profile_state > 0) {
|
||||
print_status();
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void OvenCtl::print_status() {
|
||||
if (error_condition == 0) {
|
||||
String tmp("T: ");
|
||||
if (time < 10)
|
||||
tmp += "00";
|
||||
else if (time < 100)
|
||||
tmp += "0";
|
||||
tmp += time;
|
||||
tmp += " Tmp: ";
|
||||
if (temperature < 10)
|
||||
tmp += "00";
|
||||
else if (temperature < 100)
|
||||
tmp += "0";
|
||||
tmp += temperature;
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print(tmp);
|
||||
|
||||
tmp = "Profile: ";
|
||||
tmp += profile_state;
|
||||
tmp += "/";
|
||||
tmp += END_STATE;
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(tmp);
|
||||
lcd.setCursor(13, 1);
|
||||
if (is_oven_heating)
|
||||
lcd.print("on ");
|
||||
else
|
||||
lcd.print("off");
|
||||
}
|
||||
else {
|
||||
lcd.clear();
|
||||
lcd.print("Error:");
|
||||
lcd.setCursor(0, 1);
|
||||
if (error_condition & E_DT_MIN)
|
||||
lcd.print("K/s too low");
|
||||
if (error_condition & E_DT_MAX)
|
||||
lcd.print("K/s too high");
|
||||
if (error_condition & E_TIME_MAX)
|
||||
lcd.print("reflow too long");
|
||||
if (error_condition & E_TS_TOO_SHORT)
|
||||
lcd.print("ts too short");
|
||||
if (error_condition & E_TS_TOO_LONG)
|
||||
lcd.print("ts too long");
|
||||
if (error_condition & E_TL_TOO_SHORT)
|
||||
lcd.print("tal too short");
|
||||
if (error_condition & E_TL_TOO_LONG)
|
||||
lcd.print("tal too long");
|
||||
if (error_condition & E_TP_TOO_LONG)
|
||||
lcd.print("peak too short");
|
||||
if (error_condition & E_TP_TOO_SHORT)
|
||||
lcd.print("peak too long");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::control_oven() {
|
||||
if (temperature <= set_min + actual_hysteresis && !is_oven_heating) {
|
||||
is_oven_heating = true;
|
||||
// Serial.println("Oven turned on");
|
||||
}
|
||||
else if (temperature >= set_min + actual_hysteresis && is_oven_heating) {
|
||||
is_oven_heating = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::set_temp(int min, int max, int dt_min, int dt_max) {
|
||||
set_min = min;
|
||||
set_max = max;
|
||||
set_dt_min = dt_min;
|
||||
set_dt_max = dt_max;
|
||||
}
|
||||
|
||||
|
||||
void OvenCtl::get_temp() {
|
||||
last_temperature = temperature;
|
||||
temperature = int(float(analogRead(2)) * 0.2929);
|
||||
actual_dt = float(temperature) - last_temperature;
|
||||
}
|
||||
|
||||
void OvenCtl::check_dt() {
|
||||
if (disable_checks)
|
||||
return;
|
||||
if (actual_dt > set_dt_max) {
|
||||
error_condition |= E_DT_MAX;
|
||||
}
|
||||
if (actual_dt < set_dt_min) {
|
||||
error_condition |= E_DT_MIN;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
#ifndef _H_OVEN_CTL
|
||||
#define _H_OVEN_CTL
|
||||
|
||||
/*
|
||||
// operational states
|
||||
#define OP_CONFIG 0
|
||||
#define OP_STAND_ALONE 1
|
||||
#define OP_REMOTE 2*/
|
||||
|
||||
enum OP_STATE {
|
||||
OP_CONFIG,
|
||||
OP_STAND_ALONE,
|
||||
OP_REMOTE
|
||||
};
|
||||
|
||||
#define E_DT_MIN 1 // temperature dt too small
|
||||
#define E_DT_MAX 2 // temperature dt too big
|
||||
#define E_TIME_MAX 4 // reflow process does take too long
|
||||
#define E_TS_TOO_SHORT 8 // Ts duration too short
|
||||
#define E_TS_TOO_LONG 16 // Ts duration too long
|
||||
#define E_TL_TOO_SHORT 32 // Tl duration too short
|
||||
#define E_TL_TOO_LONG 64 // Tl duration too long
|
||||
#define E_TP_TOO_SHORT 128 // Tp duration too short
|
||||
#define E_TP_TOO_LONG 256 // Tp duration too long
|
||||
#define E_CONFIG 512 // error happened in config state
|
||||
|
||||
|
||||
// profile states
|
||||
enum PROFILE_STATE {
|
||||
START_STATE,
|
||||
PREHEAT_STATE,
|
||||
RAMP_UP_STATE,
|
||||
TAL_FIRST_STATE,
|
||||
PEAK_STATE,
|
||||
TAL_SECOND_STATE,
|
||||
RAMP_DOWN_STATE,
|
||||
END_STATE,
|
||||
ERROR_STATE
|
||||
};
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class LiquidCrystal;
|
||||
class DFR_Key;
|
||||
class Profile;
|
||||
|
||||
extern Profile profile;
|
||||
|
||||
class OvenCtl {
|
||||
public:
|
||||
|
||||
OvenCtl();
|
||||
void loop();
|
||||
// void set_config_state();
|
||||
|
||||
private:
|
||||
// system time, timestamps and temperatures from sensors
|
||||
int time; // profile seconds
|
||||
int temperature; // actual oven temp
|
||||
int last_temperature; // last oven temp
|
||||
float actual_dt; // actual difference from last to actual temperatur
|
||||
|
||||
// timestamps of event beginnings/ends
|
||||
int Ts_time_start;
|
||||
int Ts_time_end;
|
||||
int Tl_time_start;
|
||||
int Tl_time_end;
|
||||
int Tp_time_start;
|
||||
int Tp_time_end;
|
||||
|
||||
// thermostat
|
||||
float set_min;
|
||||
float set_max;
|
||||
int set_dt_min;
|
||||
int set_dt_max;
|
||||
|
||||
float ramp_up_hysteresis; // duration in seconds
|
||||
float ramp_down_hysteresis; // duration in seconds
|
||||
float actual_hysteresis; // duration in seconds
|
||||
|
||||
// ui stuff
|
||||
boolean disable_checks;
|
||||
|
||||
// state machine
|
||||
unsigned int error_condition;
|
||||
OP_STATE op_state;
|
||||
PROFILE_STATE profile_state;
|
||||
boolean is_oven_heating;
|
||||
|
||||
void print_status();
|
||||
void control_oven();
|
||||
void set_temp(int, int, int, int);
|
||||
void get_temp();
|
||||
void check_dt();
|
||||
void check_max_duration();
|
||||
|
||||
// void set_start_state();
|
||||
// void set_preheat_state();
|
||||
// void set_tal_first_state();
|
||||
// void set_ramp_up_state();
|
||||
// void set_peak_state();
|
||||
// void set_tal_second_state();
|
||||
// void set_ramp_down_state();
|
||||
// void set_end_state();
|
||||
// void set_error_state();
|
||||
|
||||
// void handle_profile_states();
|
||||
void handle_stand_alone_state();
|
||||
void handle_remote_state();
|
||||
|
||||
// void handle_config_state();
|
||||
// void handle_start_state();
|
||||
// void handle_ramp_up_state();
|
||||
// void handle_preheat_state();
|
||||
// void handle_tal_first_state();
|
||||
// void handle_peak_state();
|
||||
// void handle_tal_second_state();
|
||||
// void handle_ramp_down_state();
|
||||
// void handle_end_state();
|
||||
// void handle_error_state();
|
||||
|
||||
// void check_Ts_duration_min();
|
||||
// void check_Ts_duration_max();
|
||||
// void check_Tl_duration_min();
|
||||
// void check_Tl_duration_max();
|
||||
// void check_Tp_duration_min();
|
||||
// void check_Tp_duration_max();
|
||||
|
||||
void send_state();
|
||||
void send_config();
|
||||
void reset();
|
||||
|
||||
void recv_config();
|
||||
|
||||
void dispatch_input_config(int);
|
||||
};
|
||||
|
||||
#endif
|
477
plot.py
477
plot.py
|
@ -1,30 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
GP:
|
||||
Changed datasource, title, and refresh interval to use
|
||||
as a poor man's Arduino oscilliscope.
|
||||
|
||||
This demo demonstrates how to draw a dynamic mpl (matplotlib)
|
||||
plot in a wxPython application.
|
||||
|
||||
It allows "live" plotting as well as manual zooming to specific
|
||||
regions.
|
||||
|
||||
Both X and Y axes allow "auto" or "manual" settings. For Y, auto
|
||||
mode sets the scaling of the graph to see all the data points.
|
||||
For X, auto mode makes the graph "follow" the data. Set it X min
|
||||
to manual 0 to always see the whole data from the beginning.
|
||||
|
||||
Note: press Enter in the 'manual' text box to make a new value
|
||||
affect the plot.
|
||||
|
||||
Eli Bendersky (eliben@gmail.com)
|
||||
License: this code is in the public domain
|
||||
Last modified: 31.07.2008
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
import pprint
|
||||
import random
|
||||
|
@ -33,85 +8,40 @@ import wx
|
|||
|
||||
REFRESH_INTERVAL_MS = 1000
|
||||
|
||||
# The recommended way to use wx with mpl is with the WXAgg
|
||||
# backend.
|
||||
#
|
||||
import matplotlib
|
||||
matplotlib.use('WXAgg')
|
||||
import matplotlib.lines
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.pyplot import legend
|
||||
from matplotlib.backends.backend_wxagg import \
|
||||
FigureCanvasWxAgg as FigCanvas, \
|
||||
NavigationToolbar2WxAgg as NavigationToolbar
|
||||
from matplotlib.path import Path
|
||||
import matplotlib.patches as patches
|
||||
import wx.lib.buttons as buttons
|
||||
|
||||
import numpy as np
|
||||
import pylab
|
||||
#Data comes from here
|
||||
|
||||
from Arduino_Monitor import SerialData as DataGen
|
||||
|
||||
|
||||
class BoundControlBox(wx.Panel):
|
||||
""" A static box with a couple of radio buttons and a text
|
||||
box. Allows to switch between an automatic mode and a
|
||||
manual mode with an associated value.
|
||||
"""
|
||||
def __init__(self, parent, ID, label, initval):
|
||||
wx.Panel.__init__(self, parent, ID)
|
||||
|
||||
self.value = initval
|
||||
|
||||
box = wx.StaticBox(self, -1, label)
|
||||
sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
|
||||
|
||||
self.radio_auto = wx.RadioButton(self, -1,
|
||||
label="Auto", style=wx.RB_GROUP)
|
||||
self.radio_manual = wx.RadioButton(self, -1,
|
||||
label="Manual")
|
||||
self.manual_text = wx.TextCtrl(self, -1,
|
||||
size=(35,-1),
|
||||
value=str(initval),
|
||||
style=wx.TE_PROCESS_ENTER)
|
||||
|
||||
self.Bind(wx.EVT_UPDATE_UI, self.on_update_manual_text, self.manual_text)
|
||||
self.Bind(wx.EVT_TEXT_ENTER, self.on_text_enter, self.manual_text)
|
||||
|
||||
manual_box = wx.BoxSizer(wx.HORIZONTAL)
|
||||
manual_box.Add(self.radio_manual, flag=wx.ALIGN_CENTER_VERTICAL)
|
||||
manual_box.Add(self.manual_text, flag=wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
sizer.Add(self.radio_auto, 0, wx.ALL, 10)
|
||||
sizer.Add(manual_box, 0, wx.ALL, 10)
|
||||
self.radio_auto.SetValue(False);
|
||||
self.radio_manual.SetValue(True);
|
||||
|
||||
self.SetSizer(sizer)
|
||||
sizer.Fit(self)
|
||||
|
||||
def on_update_manual_text(self, event):
|
||||
self.manual_text.Enable(self.radio_manual.GetValue())
|
||||
|
||||
def on_text_enter(self, event):
|
||||
self.value = self.manual_text.GetValue()
|
||||
|
||||
def is_auto(self):
|
||||
return self.radio_auto.GetValue()
|
||||
|
||||
def manual_value(self):
|
||||
return self.value
|
||||
import Arduino_Monitor
|
||||
|
||||
|
||||
class GraphFrame(wx.Frame):
|
||||
""" The main frame of the application
|
||||
"""
|
||||
title = 'Demo: dynamic matplotlib graph'
|
||||
title = 'reflowctl gui'
|
||||
|
||||
def __init__(self):
|
||||
wx.Frame.__init__(self, None, -1, self.title)
|
||||
|
||||
self.datagen = DataGen()
|
||||
self.data = [self.datagen.next()]
|
||||
self.paused = False
|
||||
self.started = False
|
||||
|
||||
self.profile = []
|
||||
self.state = []
|
||||
self.count = 0
|
||||
|
||||
self.create_menu()
|
||||
self.create_status_bar()
|
||||
|
@ -121,6 +51,7 @@ class GraphFrame(wx.Frame):
|
|||
self.Bind(wx.EVT_TIMER, self.on_redraw_timer, self.redraw_timer)
|
||||
self.redraw_timer.Start(REFRESH_INTERVAL_MS)
|
||||
|
||||
|
||||
def create_menu(self):
|
||||
self.menubar = wx.MenuBar()
|
||||
|
||||
|
@ -137,110 +68,274 @@ class GraphFrame(wx.Frame):
|
|||
def create_main_panel(self):
|
||||
self.panel = wx.Panel(self)
|
||||
|
||||
self.hbox1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.init_profile()
|
||||
self.init_log()
|
||||
self.init_oven_status()
|
||||
self.init_plot()
|
||||
self.canvas = FigCanvas(self.panel, -1, self.fig)
|
||||
|
||||
self.xmin_control = BoundControlBox(self.panel, -1, "X min", 0)
|
||||
self.xmax_control = BoundControlBox(self.panel, -1, "X max", 250)
|
||||
self.ymin_control = BoundControlBox(self.panel, -1, "Y min", 0)
|
||||
self.ymax_control = BoundControlBox(self.panel, -1, "Y max", 280)
|
||||
self.recv_config_button = wx.Button(self.panel, -1, "Receive Config")
|
||||
self.Bind(wx.EVT_BUTTON, self.on_recv_config_button, self.recv_config_button)
|
||||
|
||||
self.pause_button = wx.Button(self.panel, -1, "Pause")
|
||||
self.Bind(wx.EVT_BUTTON, self.on_pause_button, self.pause_button)
|
||||
self.Bind(wx.EVT_UPDATE_UI, self.on_update_pause_button, self.pause_button)
|
||||
self.send_button = wx.Button(self.panel, -1, "Send Config")
|
||||
self.Bind(wx.EVT_BUTTON, self.on_send_button, self.send_button)
|
||||
|
||||
self.cb_grid = wx.CheckBox(self.panel, -1,
|
||||
"Show Grid",
|
||||
style=wx.ALIGN_RIGHT)
|
||||
self.Bind(wx.EVT_CHECKBOX, self.on_cb_grid, self.cb_grid)
|
||||
self.cb_grid.SetValue(True)
|
||||
self.start_button = buttons.GenToggleButton(self.panel, -1, "Start")
|
||||
self.Bind(wx.EVT_BUTTON, self.on_start_button, self.start_button)
|
||||
|
||||
self.cb_xlab = wx.CheckBox(self.panel, -1,
|
||||
"Show X labels",
|
||||
style=wx.ALIGN_RIGHT)
|
||||
self.Bind(wx.EVT_CHECKBOX, self.on_cb_xlab, self.cb_xlab)
|
||||
self.cb_xlab.SetValue(True)
|
||||
#self.on_bitmap = wx.Image('burn.png', wx.BITMAP_TYPE_PNG).Scale(32, 32, wx.IMAGE_QUALITY_HIGH).ConvertToBitmap()
|
||||
#self.off_bitmap = wx.Image('unburn.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap()
|
||||
|
||||
self.hbox1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.hbox1.Add(self.pause_button, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL)
|
||||
self.hbox1.AddSpacer(5)
|
||||
self.hbox1.Add(self.cb_grid, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL)
|
||||
self.hbox1.AddSpacer(5)
|
||||
self.hbox1.Add(self.cb_xlab, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
self.hbox2 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.hbox2.Add(self.xmin_control, border=5, flag=wx.ALL)
|
||||
self.hbox2.Add(self.xmax_control, border=5, flag=wx.ALL)
|
||||
self.hbox2.AddSpacer(24)
|
||||
self.hbox2.Add(self.ymin_control, border=5, flag=wx.ALL)
|
||||
self.hbox2.Add(self.ymax_control, border=5, flag=wx.ALL)
|
||||
self.ctrls = wx.BoxSizer(wx.VERTICAL)
|
||||
self.ctrls.Add(self.recv_config_button, border=5, flag=wx.ALL | wx.ALIGN_LEFT)
|
||||
self.ctrls.Add(self.send_button, border=5, flag=wx.ALL | wx.ALIGN_LEFT)
|
||||
self.ctrls.Add(self.start_button, border=5, flag=wx.ALL | wx.ALIGN_LEFT)
|
||||
self.hbox1.Add(self.ctrls, border=5, flag=wx.ALL | wx.ALIGN_TOP)
|
||||
|
||||
self.vbox = wx.BoxSizer(wx.VERTICAL)
|
||||
self.vbox.Add(self.hbox1, 0, flag=wx.ALIGN_LEFT | wx.ALL)
|
||||
self.vbox.Add(self.canvas, 1, flag=wx.LEFT | wx.TOP | wx.GROW)
|
||||
self.vbox.Add(self.hbox1, 0, flag=wx.ALIGN_LEFT | wx.TOP)
|
||||
self.vbox.Add(self.hbox2, 0, flag=wx.ALIGN_LEFT | wx.TOP)
|
||||
#self.vbox.Add(self.hbox1, 0, flag=wx.ALIGN_LEFT | wx.TOP)
|
||||
|
||||
|
||||
self.panel.SetSizer(self.vbox)
|
||||
self.vbox.Fit(self)
|
||||
|
||||
def profile_spin_changed(self, event):
|
||||
print dir(event)
|
||||
|
||||
|
||||
def add_profile_item(self, title, sizer, min_=1, max_=250):
|
||||
|
||||
mc = 8
|
||||
|
||||
item = wx.SpinCtrl(self.panel, -1, "", (30, 50))
|
||||
item.SetRange(min_, max_)
|
||||
item.SetValue(Arduino_Monitor.profile[self.count])
|
||||
|
||||
self.Bind(wx.EVT_SPIN, self.profile_spin_changed, item)
|
||||
|
||||
sizer.Add(wx.StaticText(self.panel, -1, title), (self.count, 0))
|
||||
sizer.Add(item, (self.count, 1))
|
||||
|
||||
self.count += 1
|
||||
self.profile.append(item)
|
||||
|
||||
def init_profile(self):
|
||||
self.preheat_sizer = wx.GridBagSizer(5, 5)
|
||||
self.rampup_sizer = wx.GridBagSizer(5, 5)
|
||||
self.peak_sizer = wx.GridBagSizer(5, 5)
|
||||
self.rampdown_sizer = wx.GridBagSizer(5, 5)
|
||||
|
||||
self.add_profile_item("Ts_min (°C)", self.preheat_sizer, 0, 300)
|
||||
self.add_profile_item("Ts_max (°C)", self.preheat_sizer, 0, 300)
|
||||
self.add_profile_item("Ts duration min (s)", self.preheat_sizer, 0, 300)
|
||||
self.add_profile_item("Ts duration max (s)", self.preheat_sizer, 0, 300)
|
||||
|
||||
self.add_profile_item("ts ramp up min (°C/s)", self.preheat_sizer, 1, 100)
|
||||
self.add_profile_item("ts ramp up max (°C/s)", self.preheat_sizer, 1, 100)
|
||||
self.add_profile_item("tp ramp up min (°C/s)", self.peak_sizer, 1, 100)
|
||||
self.add_profile_item("tp ramp up max (°C/s)", self.peak_sizer1, 100)
|
||||
|
||||
self.add_profile_item("Tl duration min (s)", self.rampup_sizer, 0, 300)
|
||||
self.add_profile_item("Tl duration max (s)", self.rampup_sizer, 0, 300)
|
||||
|
||||
self.add_profile_item("Tp (°C)", self.peak_sizer, 0, 300)
|
||||
self.add_profile_item("Tp duration min (s)", self.peak_sizer, 0, 300)
|
||||
self.add_profile_item("Tp duration max (s)", self.peak_sizer, 0, 300)
|
||||
|
||||
self.add_profile_item("ramp down min (°C/s)", self.rampdown_sizer, -100, 0)
|
||||
self.add_profile_item("ramp down max (°C/s)", self.rampdown_sizer, -100, 0)
|
||||
|
||||
self.add_profile_item("time max (s)", 0, 800)
|
||||
|
||||
self.box = wx.StaticBox(self.panel, -1, "Profile Settings")
|
||||
self.bsizer = wx.StaticBoxSizer(self.box, wx.VERTICAL)
|
||||
self.bsizer.Add(self.profile_sizer, 0, flag=wx.ALL, border=5)
|
||||
self.hbox1.Add(self.bsizer, border=5, flag=wx.ALL | wx.ALIGN_TOP)
|
||||
|
||||
def init_oven_status(self):
|
||||
self.oven_status_sizer = wx.GridBagSizer(5, 5)
|
||||
|
||||
#set_min = 0;
|
||||
#set_max = 0;
|
||||
#set_dt_min = 0;
|
||||
#set_dt_max = 0;
|
||||
|
||||
self.oven_status_sizer.Add(wx.StaticText(self.panel, -1, "Connected"), (0, 0))
|
||||
self.oven_connected = wx.StaticText(self.panel, -1, str(self.datagen.connected()))
|
||||
self.oven_status_sizer.Add(self.oven_connected, (0, 1))
|
||||
|
||||
self.oven_status_sizer.Add(wx.StaticText(self.panel, -1, "Temperature"), (1, 0))
|
||||
self.temperature = wx.TextCtrl(self.panel, -1, str(Arduino_Monitor.status[1]))
|
||||
self.oven_status_sizer.Add(self.temperature, (1, 1))
|
||||
|
||||
self.oven_status_sizer.Add(wx.StaticText(self.panel, -1, "Time"), (2, 0))
|
||||
self.time = wx.TextCtrl(self.panel, -1, str(Arduino_Monitor.status[0]))
|
||||
self.oven_status_sizer.Add(self.time, (2, 1))
|
||||
|
||||
|
||||
self.oven_status_sizer.Add(wx.StaticText(self.panel, -1, "State"), (3, 0))
|
||||
self.pstate = wx.TextCtrl(self.panel, -1, str(Arduino_Monitor.status[3]))
|
||||
self.oven_status_sizer.Add(self.pstate, (3, 1))
|
||||
|
||||
self.oven_status_sizer.Add(wx.StaticText(self.panel, -1, "Error"), (4, 0))
|
||||
self.perror = wx.TextCtrl(self.panel, -1, str(Arduino_Monitor.status[4]))
|
||||
self.oven_status_sizer.Add(self.perror, (4, 1))
|
||||
|
||||
self.oven_status_sizer.Add(wx.StaticText(self.panel, -1, "Heating"), (5, 0))
|
||||
self.is_oven_heating = wx.TextCtrl(self.panel, -1, str(Arduino_Monitor.status[5]))
|
||||
self.oven_status_sizer.Add(self.is_oven_heating, (5, 1))
|
||||
|
||||
self.obox = wx.StaticBox(self.panel, -1, "Oven status")
|
||||
self.osizer = wx.StaticBoxSizer(self.obox, wx.VERTICAL)
|
||||
self.osizer.Add(self.oven_status_sizer, 0, flag=wx.ALL, border=5)
|
||||
self.hbox1.Add(self.osizer, border=5, flag=wx.ALL | wx.ALIGN_TOP)
|
||||
|
||||
|
||||
def init_log(self):
|
||||
self.log_sizer = wx.GridBagSizer(5, 5)
|
||||
|
||||
self.log_sizer.Add(wx.StaticText(self.panel, -1, "Ts_time_start"), (0, 0))
|
||||
self.ts_time_start = wx.TextCtrl(self.panel, -1)
|
||||
self.log_sizer.Add(self.ts_time_start, (0, 1))
|
||||
|
||||
self.log_sizer.Add(wx.StaticText(self.panel, -1, "Ts_time_end"), (1, 0))
|
||||
self.ts_time_end = wx.TextCtrl(self.panel, -1)
|
||||
self.log_sizer.Add(self.ts_time_end, (1, 1))
|
||||
|
||||
self.log_sizer.Add(wx.StaticText(self.panel, -1, "Tl_time_start"), (2, 0))
|
||||
self.tl_time_start = wx.TextCtrl(self.panel, -1)
|
||||
self.log_sizer.Add(self.tl_time_start, (2, 1))
|
||||
|
||||
self.log_sizer.Add(wx.StaticText(self.panel, -1, "Tl_time_end"), (3, 0))
|
||||
self.tl_time_end = wx.TextCtrl(self.panel, -1)
|
||||
self.log_sizer.Add(self.tl_time_end, (3, 1))
|
||||
|
||||
|
||||
self.log_sizer.Add(wx.StaticText(self.panel, -1, "Tp_time_start"), (4, 0))
|
||||
self.tp_time_start = wx.TextCtrl(self.panel, -1)
|
||||
self.log_sizer.Add(self.tp_time_start, (4, 1))
|
||||
|
||||
self.log_sizer.Add(wx.StaticText(self.panel, -1, "Tp_time_end"), (5, 0))
|
||||
self.tp_time_end = wx.TextCtrl(self.panel, -1)
|
||||
self.log_sizer.Add(self.tp_time_end, (5, 1))
|
||||
|
||||
self.lbox = wx.StaticBox(self.panel, -1, "Profile Log")
|
||||
self.lsizer = wx.StaticBoxSizer(self.lbox, wx.VERTICAL)
|
||||
self.lsizer.Add(self.log_sizer, 0, flag=wx.ALL, border=5)
|
||||
self.hbox1.Add(self.lsizer, border=5, flag=wx.ALL | wx.ALIGN_TOP)
|
||||
|
||||
def create_status_bar(self):
|
||||
self.statusbar = self.CreateStatusBar()
|
||||
|
||||
def init_plot(self):
|
||||
self.dpi = 100
|
||||
self.fig = Figure((3.0, 3.0), dpi=self.dpi)
|
||||
self.fig = Figure((4.0, 4.0), dpi=self.dpi)
|
||||
|
||||
self.axes = self.fig.add_subplot(111)
|
||||
self.axes.set_axis_bgcolor('black')
|
||||
self.axes.set_title(u'Reflow Temperature', size=12)
|
||||
self.axes.set_xlabel(u'Time / seconds', size=12)
|
||||
self.axes.set_ylabel(u'Temperature / °C', size=12)
|
||||
self.axes.set_ylabel(u'Temperature (°C)', size=12)
|
||||
|
||||
pylab.setp(self.axes.get_xticklabels(), fontsize=8)
|
||||
pylab.setp(self.axes.get_yticklabels(), fontsize=8)
|
||||
|
||||
# plot the data as a line series, and save the reference
|
||||
# to the plotted line series
|
||||
#
|
||||
|
||||
# no 1
|
||||
ts_min_x_min = Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MIN] / Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_UP_MAX]
|
||||
ts_min_y_min = ts_min
|
||||
ts_min_y_min = Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MIN]
|
||||
|
||||
# no 2
|
||||
ts_max_x_min = ts_min_x_min + Arduino_Monitor.profile[Arduino_Monitor.PI_TS_DURATION_MIN]
|
||||
ts_max_y_min = ts_max
|
||||
ts_max_y_min = Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MAX]
|
||||
|
||||
# no t1
|
||||
ts_max_x_max = ts_min_x_min + Arduino_Monitor.profile[Arduino_Monitor.PI_TS_DURATION_MAX]
|
||||
ts_max_y_max = ts_max
|
||||
ts_max_y_max = Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MAX]
|
||||
|
||||
ts_min_x_max = ts_max_x_max - (ts_max_y_max - ts_min_y) / Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_UP_MAX])
|
||||
ts_min_y_max = ts_min
|
||||
# no t2
|
||||
ts_min_x_max = ts_max_x_max - (ts_max_y_max - ts_min_y_min) / Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_UP_MAX]
|
||||
ts_min_y_max = Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MIN]
|
||||
|
||||
tl_x_min = ts_max_x_min + (Arduino_Monitor.profile[Arduino_Monitor.PI_TL] - Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MAX]) / Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_UP_MAX]
|
||||
tl_x_min = Arduino_Monitor.profile[Arduino_Monitor.PI_TL]
|
||||
# no 10
|
||||
t0_x_max = ts_min_x_max - (ts_max_x_max / Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_UP_MAX])
|
||||
t0_y_max = 0
|
||||
|
||||
tl_x_min = ts_max_x_min + (Arduino_Monitor.profile[Arduino_Monitor.PI_TL] - Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MAX]) / Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_UP_MAX]
|
||||
tl_y_min = Arduino_Monitor.profile[Arduino_Monitor.PI_TL]
|
||||
# no 4
|
||||
tp_x_min = ts_max_x_min + (Arduino_Monitor.profile[Arduino_Monitor.PI_TP] - ts_max_y_min) / Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_UP_MAX]
|
||||
tp_y_min = Arduino_Monitor.profile[Arduino_Monitor.PI_TP]
|
||||
|
||||
tl_x_max = tl_x_min + Arduino_Monitor.profile[Arduino_Monitor.PI_TL_DURATION_MIN]
|
||||
tl_y_max = Arduino_Monitor.profile[Arduino_Monitor.PI_TL]
|
||||
# no 5
|
||||
tp_x_max = tp_x_min + Arduino_Monitor.profile[Arduino_Monitor.PI_TP_DURATION_MAX]
|
||||
tp_y_max = tp_y_min
|
||||
|
||||
# no 8
|
||||
tp5_x_max = tp_x_min + Arduino_Monitor.profile[Arduino_Monitor.PI_TP_DURATION_MIN]
|
||||
tp5_y_max = tp_y_max - 5
|
||||
|
||||
# no 9
|
||||
tp5_x_min = ts_max_x_max + (tp5_y_max - ts_max_y_max) / Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_UP_MAX]
|
||||
tp5_y_min = tp5_y_max
|
||||
|
||||
# no 6
|
||||
end_x_max = tp_x_max + tp_y_max / abs(Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_DOWN_MIN])
|
||||
end_y_max = 0
|
||||
|
||||
self.xmax = end_x_max + 20
|
||||
self.ymax = Arduino_Monitor.profile[Arduino_Monitor.PI_TP] + 20
|
||||
|
||||
# no 7
|
||||
end_x_min = tp5_x_max + tp5_y_max / abs(Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_DOWN_MAX])
|
||||
end_y_min = 0
|
||||
|
||||
tsmin = Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MIN]
|
||||
tsmax = Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MAX]
|
||||
tl = Arduino_Monitor.profile[Arduino_Monitor.PI_TL]
|
||||
tp = Arduino_Monitor.profile[Arduino_Monitor.PI_TP]
|
||||
self.ts_line_min = matplotlib.lines.Line2D([0, self.xmax], [tsmin, tsmin],
|
||||
transform=self.axes.transData, figure=self.fig, color='green')
|
||||
self.ts_line_max = matplotlib.lines.Line2D([0, self.xmax], [tsmax, tsmax],
|
||||
transform=self.axes.transData, figure=self.fig, label="Ts_max", color='lightgreen')
|
||||
self.tl_line = matplotlib.lines.Line2D([0, self.xmax], [tl, tl],
|
||||
transform=self.axes.transData, figure=self.fig, label="Tl", color='yellow')
|
||||
self.tp_line = matplotlib.lines.Line2D([0, self.xmax], [tp, tp],
|
||||
transform=self.axes.transData, figure=self.fig, label="Tp", color='blue')
|
||||
|
||||
self.ts_line_min.set_label("Ts_min")
|
||||
self.ts_line_min.set_label("Ts_max")
|
||||
self.tl_line.set_label("Tl")
|
||||
self.tp_line.set_label("Tp")
|
||||
self.fig.lines.extend([self.ts_line_min, self.ts_line_max, self.tl_line, self.tp_line])
|
||||
|
||||
verts = [
|
||||
[ 0.0, 0.0],
|
||||
[ 75.0, 150.0],
|
||||
[100.0, 200.0],
|
||||
[108.5, 217.0],
|
||||
[130.0, 260.0],
|
||||
[170.0, 260.0],
|
||||
[300.0, 0.0],
|
||||
[ 0.0, 0.0]]
|
||||
[0.0, 0.0],
|
||||
[ts_min_x_min, ts_min_y_min],
|
||||
[ts_max_x_min, ts_max_y_min],
|
||||
[ts_max_x_max, ts_max_y_max],
|
||||
[ts_min_x_max, ts_min_y_max],
|
||||
#[tp_x_min, tp_y_min],
|
||||
#[tp_x_max, tp_y_max],
|
||||
#[end_x_max, end_y_max],
|
||||
#[end_x_min, end_y_min],
|
||||
#[tp5_x_max, tp5_y_max],
|
||||
#[tp5_x_min, tp5_y_min],
|
||||
[t0_x_max, t0_y_max],
|
||||
[0.0, 0.0]]
|
||||
|
||||
codes = [Path.MOVETO,
|
||||
Path.LINETO,
|
||||
codes = [
|
||||
Path.MOVETO,
|
||||
Path.LINETO,
|
||||
Path.LINETO,
|
||||
Path.LINETO,
|
||||
Path.LINETO,
|
||||
Path.LINETO,
|
||||
#Path.LINETO,
|
||||
#Path.LINETO,
|
||||
#Path.LINETO,
|
||||
#Path.LINETO,
|
||||
Path.CLOSEPOLY]
|
||||
|
||||
self.plot_data = self.axes.plot(
|
||||
|
@ -249,86 +344,62 @@ class GraphFrame(wx.Frame):
|
|||
color=(1, 1, 0),
|
||||
)[0]
|
||||
|
||||
print "verts", verts
|
||||
|
||||
|
||||
path = Path(verts, codes)
|
||||
self.patch = patches.PathPatch(path, edgecolor="red", facecolor='orange', lw=2)
|
||||
self.axes.add_patch(self.patch)
|
||||
self.axes.legend( (self.ts_line_min, self.ts_line_max, self.tl_line, self.tp_line), ('Ts_min', 'Ts_max', 'Tl', 'Tp'), loc=2)
|
||||
|
||||
def draw_plot(self):
|
||||
""" Redraws the plot
|
||||
"""
|
||||
# when xmin is on auto, it "follows" xmax to produce a
|
||||
# sliding window effect. therefore, xmin is assigned after
|
||||
# xmax.
|
||||
#
|
||||
if self.xmax_control.is_auto():
|
||||
xmax = len(self.data) if len(self.data) > 50 else 50
|
||||
else:
|
||||
xmax = int(self.xmax_control.manual_value())
|
||||
|
||||
if self.xmin_control.is_auto():
|
||||
xmin = xmax - 50
|
||||
else:
|
||||
xmin = int(self.xmin_control.manual_value())
|
||||
self.axes.set_xbound(lower=0, upper=self.xmax)
|
||||
self.axes.set_ybound(lower=0, upper=self.ymax)
|
||||
|
||||
#xmax = 480
|
||||
self.axes.grid(True, color='gray')
|
||||
|
||||
# for ymin and ymax, find the minimal and maximal values
|
||||
# in the data set and add a mininal margin.
|
||||
#
|
||||
# note that it's easy to change this scheme to the
|
||||
# minimal/maximal value in the current display, and not
|
||||
# the whole data set.
|
||||
#
|
||||
if self.ymin_control.is_auto():
|
||||
ymin = round(min(self.data), 0) - 1
|
||||
else:
|
||||
ymin = int(self.ymin_control.manual_value())
|
||||
|
||||
if self.ymax_control.is_auto():
|
||||
ymax = round(max(self.data), 0) + 1
|
||||
else:
|
||||
ymax = int(self.ymax_control.manual_value())
|
||||
|
||||
#ymax = 300
|
||||
|
||||
self.axes.set_xbound(lower=xmin, upper=xmax)
|
||||
self.axes.set_ybound(lower=ymin, upper=ymax)
|
||||
|
||||
# anecdote: axes.grid assumes b=True if any other flag is
|
||||
# given even if b is set to False.
|
||||
# so just passing the flag into the first statement won't
|
||||
# work.
|
||||
#
|
||||
if self.cb_grid.IsChecked():
|
||||
self.axes.grid(True, color='gray')
|
||||
else:
|
||||
self.axes.grid(False)
|
||||
|
||||
# Using setp here is convenient, because get_xticklabels
|
||||
# returns a list over which one needs to explicitly
|
||||
# iterate, and setp already handles this.
|
||||
#
|
||||
pylab.setp(self.axes.get_xticklabels(),
|
||||
visible=self.cb_xlab.IsChecked())
|
||||
pylab.setp(self.axes.get_xticklabels(), visible=True)
|
||||
|
||||
self.plot_data.set_xdata(np.arange(len(self.data)))
|
||||
self.plot_data.set_ydata(np.array(self.data))
|
||||
|
||||
self.canvas.draw()
|
||||
|
||||
def on_pause_button(self, event):
|
||||
self.paused = not self.paused
|
||||
def update_config(self):
|
||||
for ix, i in enumerate(self.profile):
|
||||
i.SetValue(str(Arduino_Monitor.profile[i]))
|
||||
|
||||
def on_update_pause_button(self, event):
|
||||
label = "Resume" if self.paused else "Pause"
|
||||
self.pause_button.SetLabel(label)
|
||||
def update_state(self):
|
||||
if Arduino_Monitor.status[3] > 0:
|
||||
self.started = True
|
||||
|
||||
def on_cb_grid(self, event):
|
||||
self.draw_plot()
|
||||
self.time.SetValue(str(Arduino_Monitor.status[0]))
|
||||
self.temperature.SetValue(str(Arduino_Monitor.status[1]))
|
||||
self.pstate.SetValue(str(Arduino_Monitor.status[3]))
|
||||
self.perror.SetValue(str(Arduino_Monitor.status[4]))
|
||||
self.is_oven_heating.SetValue(str(Arduino_Monitor.status[5]))
|
||||
|
||||
def on_cb_xlab(self, event):
|
||||
self.draw_plot()
|
||||
|
||||
def on_start_button(self, event):
|
||||
self.started = self.datagen.send_start()
|
||||
self.recv_config_button.Disable()
|
||||
self.send_button.Disable()
|
||||
self.profile = []
|
||||
for i in range(30):
|
||||
self.profile_sizer.Remove(i)
|
||||
self.profile_sizer.Layout()
|
||||
|
||||
def on_recv_config_button(self, event):
|
||||
if not self.started:
|
||||
self.datagen.recv_config()
|
||||
|
||||
|
||||
def on_send_button(self, event):
|
||||
if not self.started:
|
||||
self.datagen.send_config()
|
||||
|
||||
def on_save_plot(self, event):
|
||||
file_choices = "PNG (*.png)|*.png"
|
||||
|
@ -347,12 +418,11 @@ class GraphFrame(wx.Frame):
|
|||
self.flash_status_message("Saved to %s" % path)
|
||||
|
||||
def on_redraw_timer(self, event):
|
||||
# if paused do not add data, but still redraw the plot
|
||||
# (to respond to scale modifications, grid change, etc.)
|
||||
#
|
||||
if not self.paused:
|
||||
|
||||
if self.started:
|
||||
self.data.append(self.datagen.next())
|
||||
|
||||
self.update_state()
|
||||
self.draw_plot()
|
||||
|
||||
def on_exit(self, event):
|
||||
|
@ -376,3 +446,4 @@ if __name__ == '__main__':
|
|||
app.frame = GraphFrame()
|
||||
app.frame.Show()
|
||||
app.MainLoop()
|
||||
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
#include "profile.h"
|
||||
#include "oven_control.h"
|
||||
#include <LiquidCrystal.h>
|
||||
#include <DFR_Key.h>
|
||||
|
||||
|
||||
|
||||
//
|
||||
// state: temp_min, temp_max, duration
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
// x0 = time of last_state.temp_max
|
||||
// y0 = time of last_state.temp_max
|
||||
|
||||
// x1 = xa + state.duration
|
||||
// y1 = state.temp_max
|
||||
|
||||
|
||||
// x = actual_time
|
||||
// y = y0 + ((x - x0) * y1 - (x - x0) * y0) / (x1 - x0)
|
||||
|
||||
// preheat, soak, tal1, soak, tal2, rampdown
|
||||
|
||||
|
||||
Profile::Profile() :
|
||||
data {150, // °C
|
||||
200, // °C
|
||||
217, // °C
|
||||
260, // 245-260°C
|
||||
480, // seconds
|
||||
|
||||
0, // ts ramp up rates
|
||||
2,
|
||||
0, // tp ramp up rates
|
||||
2,
|
||||
-1, // ramp down rates
|
||||
-6,
|
||||
|
||||
// profile temp durations
|
||||
60,
|
||||
180,
|
||||
60,
|
||||
150,
|
||||
20,
|
||||
40},
|
||||
config_index(0),
|
||||
config_state(CS_MENU),
|
||||
key(NO_KEY) {}
|
||||
|
||||
boolean Profile::handle_config_state() {
|
||||
boolean changed = false;
|
||||
|
||||
key = keypad.getKey();
|
||||
|
||||
switch (config_state) {
|
||||
case CS_MENU:
|
||||
if (key == SELECT_KEY) {
|
||||
config_state = CS_END;
|
||||
}
|
||||
else if (key > 0) {
|
||||
config_state = CS_EDIT;
|
||||
print_config_state();
|
||||
}
|
||||
break;
|
||||
case CS_EDIT:
|
||||
switch (key) {
|
||||
case LEFT_KEY:
|
||||
config_index = (config_index-1) % PI_END;
|
||||
changed = true;
|
||||
break;
|
||||
case RIGHT_KEY:
|
||||
config_index = (config_index+1) % PI_END;
|
||||
changed = true;
|
||||
break;
|
||||
case UP_KEY:
|
||||
data[config_index]++;
|
||||
changed = true;
|
||||
break;
|
||||
case DOWN_KEY:
|
||||
data[config_index]--;
|
||||
changed = true;
|
||||
break;
|
||||
case SELECT_KEY:
|
||||
config_state = CS_END;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
if (changed)
|
||||
print_config_state();
|
||||
break;
|
||||
case 2:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Profile::print_config_state_0() {
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("start | config");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("[sel] | [other]");
|
||||
}
|
||||
|
||||
void Profile::print_config_state() {
|
||||
lcd.clear();
|
||||
switch (config_index) {
|
||||
case PI_TS_MIN:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("PI_TS_MIN: ");
|
||||
lcd.print(data[PI_TS_MIN]);
|
||||
break;
|
||||
case PI_TS_MAX:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("PI_TS_MAX: ");
|
||||
lcd.print(data[PI_TS_MAX]);
|
||||
break;
|
||||
case PI_TL:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("Tl: ");
|
||||
lcd.print(data[PI_TL]);
|
||||
break;
|
||||
case PI_TP_MIN:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("Tp min: ");
|
||||
lcd.print(data[PI_TP_MIN]);
|
||||
break;
|
||||
case PI_TP_MAX:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("Tp max: ");
|
||||
lcd.print(data[PI_TP_MAX]);
|
||||
break;
|
||||
case PI_TIME_MAX:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("time_max: ");
|
||||
lcd.print(data[PI_TIME_MAX]);
|
||||
break;
|
||||
|
||||
// PROFILE TEMP PER SECOND RATES
|
||||
case PI_TS_RAMP_UP_MIN:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("ramp_up_min: ");
|
||||
lcd.print(data[PI_TS_RAMP_UP_MIN]);
|
||||
break;
|
||||
case PI_TS_RAMP_UP_MAX:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("ramp_up_max: ");
|
||||
lcd.print(data[PI_TS_RAMP_UP_MAX]);
|
||||
break;
|
||||
case PI_TP_RAMP_UP_MIN:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("ramp_up_min: ");
|
||||
lcd.print(data[PI_TP_RAMP_UP_MIN]);
|
||||
break;
|
||||
case PI_TP_RAMP_UP_MAX:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("ramp_up_max: ");
|
||||
lcd.print(data[PI_TP_RAMP_UP_MAX]);
|
||||
break;
|
||||
case PI_RAMP_DOWN_MAX:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("ramp_down_min: ");
|
||||
lcd.print(data[PI_RAMP_DOWN_MIN]);
|
||||
break;
|
||||
case PI_RAMP_DOWN_MIN:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("ramp_down_max: ");
|
||||
lcd.print(data[PI_RAMP_DOWN_MAX]);
|
||||
break;
|
||||
|
||||
// PROFILE TEMP DURATIONS
|
||||
case PI_TS_DURATION_MIN:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("Ts_duration_min: ");
|
||||
lcd.print(data[PI_TS_DURATION_MIN]);
|
||||
break;
|
||||
case PI_TS_DURATION_MAX:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("Ts_duration_max: ");
|
||||
lcd.print(data[PI_TS_DURATION_MAX]);
|
||||
break;
|
||||
case PI_TL_DURATION_MIN:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("Tl_duration_min: ");
|
||||
lcd.print(data[PI_TL_DURATION_MIN]);
|
||||
break;
|
||||
case PI_TL_DURATION_MAX:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("Tl_duration_max: ");
|
||||
lcd.print(data[PI_TL_DURATION_MAX]);
|
||||
break;
|
||||
case PI_TP_DURATION_MIN:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("Tp_duration_min: ");
|
||||
lcd.print(data[PI_TP_DURATION_MIN]);
|
||||
break;
|
||||
case PI_TP_DURATION_MAX:
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print("Tp_duration_max: ");
|
||||
lcd.print(data[PI_TP_DURATION_MAX]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void set_std_profile() {
|
||||
// ProfileState * ps = &states[0];
|
||||
// ps->temp_min = 150;
|
||||
// ps->duration = -1;
|
||||
//
|
||||
// ps = &states[1];
|
||||
// ps->temp_min = 200;
|
||||
// ps->duration = 100;
|
||||
}
|
||||
|
||||
Profile2 std_profile = Profile2();
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
#ifndef _H_PROFILE
|
||||
#define _H_PROFILE
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class LiquidCrystal;
|
||||
class DFR_Key;
|
||||
|
||||
#define PI_TS_MIN 0
|
||||
#define PI_TS_MAX 1
|
||||
|
||||
#define PI_TL 2
|
||||
|
||||
#define PI_TP_MIN 3
|
||||
#define PI_TP_MAX 4
|
||||
#define PI_TIME_MAX 5
|
||||
|
||||
// profile temp per second rates
|
||||
#define PI_TS_RAMP_UP_MIN 6
|
||||
#define PI_TS_RAMP_UP_MAX 7
|
||||
#define PI_TP_RAMP_UP_MIN 8
|
||||
#define PI_TP_RAMP_UP_MAX 9
|
||||
#define PI_RAMP_DOWN_MIN 10
|
||||
#define PI_RAMP_DOWN_MAX 11
|
||||
|
||||
// profile temp durations
|
||||
#define PI_TS_DURATION_MIN 12
|
||||
#define PI_TS_DURATION_MAX 13
|
||||
#define PI_TL_DURATION_MIN 14
|
||||
#define PI_TL_DURATION_MAX 15
|
||||
#define PI_TP_DURATION_MIN 16
|
||||
#define PI_TP_DURATION_MAX 17
|
||||
#define PI_END 18
|
||||
|
||||
// config states
|
||||
#define CS_MENU 0
|
||||
#define CS_EDIT 1
|
||||
#define CS_END 2
|
||||
|
||||
class LiquidCrystal;
|
||||
class DFR_Key;
|
||||
|
||||
extern LiquidCrystal lcd;
|
||||
extern DFR_Key keypad;
|
||||
|
||||
class Profile {
|
||||
public:
|
||||
int data[18];
|
||||
|
||||
unsigned int config_index;
|
||||
int config_state;
|
||||
int key;
|
||||
|
||||
Profile();
|
||||
boolean handle_config_state();
|
||||
void print_config_state();
|
||||
void print_config_state_0();
|
||||
};
|
||||
|
||||
|
||||
class ProfileState {
|
||||
public:
|
||||
int temp_min;
|
||||
int duration;
|
||||
int temp_max;
|
||||
String name;
|
||||
|
||||
ProfileState() : temp_min(0), duration(0), temp_max(0) {}
|
||||
ProfileState(int min, int dur, int max = -1, const char & name = NULL) : temp_min(min), duration(dur), temp_max(max), name(name) {}
|
||||
};
|
||||
|
||||
class SolderType {};
|
||||
|
||||
|
||||
|
||||
class Profile2 {
|
||||
public:
|
||||
ProfileState states[3];
|
||||
|
||||
};
|
||||
|
||||
|
||||
extern Profile2 std_profile;
|
||||
|
||||
void set_std_profile();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,172 @@
|
|||
import sys, os, random
|
||||
from PyQt4 import QtGui, QtCore
|
||||
|
||||
from numpy import arange, sin, pi, array, linspace
|
||||
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.path import Path
|
||||
import matplotlib.patches as patches
|
||||
|
||||
from scipy.interpolate import *
|
||||
|
||||
progname = os.path.basename(sys.argv[0])
|
||||
progversion = "0.1"
|
||||
|
||||
|
||||
class MyMplCanvas(FigureCanvas):
|
||||
"""Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.)."""
|
||||
def __init__(self, parent=None, width=5, height=4, dpi=100):
|
||||
fig = Figure(figsize=(width, height), dpi=dpi)
|
||||
self.axes = fig.add_subplot(111)
|
||||
# We want the axes cleared every time plot() is called
|
||||
self.axes.hold(False)
|
||||
|
||||
self.compute_initial_figure()
|
||||
|
||||
#
|
||||
FigureCanvas.__init__(self, fig)
|
||||
self.setParent(parent)
|
||||
|
||||
FigureCanvas.setSizePolicy(self,
|
||||
QtGui.QSizePolicy.Expanding,
|
||||
QtGui.QSizePolicy.Expanding)
|
||||
FigureCanvas.updateGeometry(self)
|
||||
|
||||
def compute_initial_figure(self):
|
||||
pass
|
||||
|
||||
|
||||
class MyDynamicMplCanvas(MyMplCanvas):
|
||||
"""A canvas that updates itself every second with a new plot."""
|
||||
def __init__(self, *args, **kwargs):
|
||||
MyMplCanvas.__init__(self, *args, **kwargs)
|
||||
timer = QtCore.QTimer(self)
|
||||
QtCore.QObject.connect(timer, QtCore.SIGNAL("timeout()"), self.update_figure)
|
||||
timer.start(1000)
|
||||
|
||||
def compute_initial_figure(self):
|
||||
x1_min = 150-20.
|
||||
y1_min = 150.
|
||||
|
||||
x2_min = x1_min + 100.
|
||||
y2_min = 200.
|
||||
|
||||
x3_min = x2_min + 20.
|
||||
y3_min = 220.
|
||||
|
||||
x4_min = x3_min + 249. - y3_min
|
||||
y4_min = 249.
|
||||
|
||||
x5_min = x4_min + (y4_min - y3_min) / 3 * 2
|
||||
y5_min = y3_min
|
||||
|
||||
x6_min = x5_min + y5_min-20.
|
||||
y6_min = 20.
|
||||
|
||||
p1x = array([0., x1_min, x2_min, x3_min, x4_min, x5_min, x6_min])
|
||||
p1y = array([20., y1_min, y2_min, y3_min, y4_min, y5_min, y6_min])
|
||||
|
||||
interp = pchip(p1x, p1y)
|
||||
|
||||
xx = linspace(0., x6_min, x6_min)
|
||||
|
||||
ynew = interp(xx)
|
||||
|
||||
print "len xx", len(xx)
|
||||
print "len p1x", len(p1x)
|
||||
print "xy", zip(p1x, p1y)
|
||||
print "ynew", ynew
|
||||
|
||||
dtp_min = 99999.
|
||||
dtp_max = 0.
|
||||
dtpi = -1
|
||||
|
||||
dtn_min = -99999.
|
||||
dtn_max = 0.
|
||||
dtni = -1
|
||||
for i in xrange(1, len(ynew)):
|
||||
tmp = ynew[i] - ynew[i-1]
|
||||
if tmp > 0:
|
||||
if tmp < dtp_min:
|
||||
dtp_min = tmp
|
||||
dtpi = i
|
||||
elif tmp > dtp_max:
|
||||
dtp_max = tmp
|
||||
dtpi = i
|
||||
elif tmp < 0:
|
||||
if tmp > dtn_min:
|
||||
dtn_min = tmp
|
||||
dtni = i
|
||||
elif tmp < dtn_max:
|
||||
dtn_max = tmp
|
||||
dtni = i
|
||||
print "max negative", dtn_min, dtn_max, dtni
|
||||
print "max positive", dtp_min, dtp_max, dtpi
|
||||
|
||||
self.axes.plot(p1x, p1y, "bo", xx, ynew, "r-")
|
||||
#self.axes.plot(p1x, p1y, 'r-o')
|
||||
|
||||
def update_figure(self):
|
||||
# Build a list of 4 random integers between 0 and 10 (both inclusive)
|
||||
#l = [ random.randint(0, 10) for i in xrange(4) ]
|
||||
|
||||
#self.axes.plot([0, 1, 2, 3], l, 'r')
|
||||
#self.draw()
|
||||
pass
|
||||
|
||||
|
||||
class ApplicationWindow(QtGui.QMainWindow):
|
||||
def __init__(self):
|
||||
QtGui.QMainWindow.__init__(self)
|
||||
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
||||
self.setWindowTitle("application main window")
|
||||
|
||||
self.file_menu = QtGui.QMenu('&File', self)
|
||||
self.file_menu.addAction('&Quit', self.fileQuit,
|
||||
QtCore.Qt.CTRL + QtCore.Qt.Key_Q)
|
||||
self.menuBar().addMenu(self.file_menu)
|
||||
|
||||
self.help_menu = QtGui.QMenu('&Help', self)
|
||||
self.menuBar().addSeparator()
|
||||
self.menuBar().addMenu(self.help_menu)
|
||||
|
||||
self.help_menu.addAction('&About', self.about)
|
||||
|
||||
self.main_widget = QtGui.QWidget(self)
|
||||
|
||||
l = QtGui.QVBoxLayout(self.main_widget)
|
||||
#sc = MyStaticMplCanvas(self.main_widget, width=5, height=4, dpi=100)
|
||||
dc = MyDynamicMplCanvas(self.main_widget, width=5, height=4, dpi=100)
|
||||
#l.addWidget(sc)
|
||||
l.addWidget(dc)
|
||||
|
||||
self.main_widget.setFocus()
|
||||
self.setCentralWidget(self.main_widget)
|
||||
|
||||
self.statusBar().showMessage("All hail matplotlib!", 2000)
|
||||
|
||||
def fileQuit(self):
|
||||
self.close()
|
||||
|
||||
def closeEvent(self, ce):
|
||||
self.fileQuit()
|
||||
|
||||
def about(self):
|
||||
QtGui.QMessageBox.about(self, "About %s" % progname,
|
||||
u"""%(prog)s version %(version)s
|
||||
Copyright \N{COPYRIGHT SIGN} 2005 Florent Rougon, 2006 Darren Dale
|
||||
|
||||
This program is a simple example of a Qt4 application embedding matplotlib
|
||||
canvases.
|
||||
|
||||
It may be used and modified with no restriction; raw copies as well as
|
||||
modified versions may be distributed without limitation."""
|
||||
% {"prog": progname, "version": progversion})
|
||||
|
||||
|
||||
qApp = QtGui.QApplication(sys.argv)
|
||||
|
||||
aw = ApplicationWindow()
|
||||
aw.setWindowTitle("%s" % progname)
|
||||
aw.show()
|
||||
sys.exit(qApp.exec_())
|
Binary file not shown.
After Width: | Height: | Size: 8.5 KiB |
Loading…
Reference in New Issue