From 4568bc796a6c362ebf7f72ee9a5fa4a9a3c4ba6a Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 1 Aug 2011 17:08:24 -0700 Subject: [PATCH] altosui: Add primitive UI for TeleLaunch Display status along with arm and fire buttons. Signed-off-by: Keith Packard --- altosui/AltosLaunch.java | 201 +++++++++++++ altosui/AltosLaunchUI.java | 518 ++++++++++++++++++++++++++++++++++ altosui/AltosPreferences.java | 39 +++ altosui/Makefile.am | 2 + src/ao_radio_cmac.c | 140 ++++++--- 5 files changed, 854 insertions(+), 46 deletions(-) create mode 100644 altosui/AltosLaunch.java create mode 100644 altosui/AltosLaunchUI.java diff --git a/altosui/AltosLaunch.java b/altosui/AltosLaunch.java new file mode 100644 index 00000000..77f681b8 --- /dev/null +++ b/altosui/AltosLaunch.java @@ -0,0 +1,201 @@ +/* + * Copyright © 2010 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.io.*; +import java.util.concurrent.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import javax.swing.event.*; + +public class AltosLaunch { + AltosDevice device; + AltosSerial serial; + boolean serial_started; + int launcher_serial; + int launcher_channel; + int rssi; + + final static int Unknown = -1; + final static int Good = 0; + final static int Bad = 1; + + int armed; + int igniter; + + private void start_serial() throws InterruptedException { + serial_started = true; + } + + private void stop_serial() throws InterruptedException { + if (!serial_started) + return; + serial_started = false; + if (serial == null) + return; + } + + class string_ref { + String value; + + public String get() { + return value; + } + public void set(String i) { + value = i; + } + public string_ref() { + value = null; + } + } + + private boolean get_string(String line, String label, string_ref s) { + if (line.startsWith(label)) { + String quoted = line.substring(label.length()).trim(); + + if (quoted.startsWith("\"")) + quoted = quoted.substring(1); + if (quoted.endsWith("\"")) + quoted = quoted.substring(0,quoted.length()-1); + s.set(quoted); + return true; + } else { + return false; + } + } + + public boolean status() throws InterruptedException, TimeoutException { + boolean ok = false; + if (serial == null) + return false; + string_ref status_name = new string_ref(); + start_serial(); + serial.printf("l %d %d\n", launcher_serial, launcher_channel); + for (;;) { + String line = serial.get_reply(20000); + if (line == null) + throw new TimeoutException(); + if (get_string(line, "Rssi: ", status_name)) { + try { + rssi = Altos.fromdec(status_name.get()); + } catch (NumberFormatException ne) { + } + break; + } else if (get_string(line, "Armed: ", status_name)) { + armed = Good; + String status = status_name.get(); + if (status.startsWith("igniter good")) + igniter = Good; + else if (status.startsWith("igniter bad")) + igniter = Bad; + else + igniter = Unknown; + ok = true; + } else if (get_string(line, "Disarmed: ", status_name)) { + armed = Bad; + if (status_name.get().startsWith("igniter good")) + igniter = Good; + else if (status_name.get().startsWith("igniter bad")) + igniter = Bad; + else + igniter = Unknown; + ok = true; + } else if (get_string(line, "Error ", status_name)) { + armed = Unknown; + igniter = Unknown; + ok = false; + break; + } + } + stop_serial(); + if (!ok) { + armed = Unknown; + igniter = Unknown; + } + return ok; + } + + public static String status_string(int status) { + switch (status) { + case Good: + return "good"; + case Bad: + return "open"; + } + return "unknown"; + } + + public void arm() { + if (serial == null) + return; + try { + start_serial(); + serial.printf("a %d %d\n", launcher_serial, launcher_channel); + serial.flush_output(); + } catch (InterruptedException ie) { + } finally { + try { + stop_serial(); + } catch (InterruptedException ie) { + } + } + } + + public void fire() { + if (serial == null) + return; + try { + start_serial(); + serial.printf("i %d %d\n", launcher_serial, launcher_channel); + serial.flush_output(); + } catch (InterruptedException ie) { + } finally { + try { + stop_serial(); + } catch (InterruptedException ie) { + } + } + } + + public void close() { + try { + stop_serial(); + } catch (InterruptedException ie) { + } + serial.close(); + serial = null; + } + + public void set_frame(Frame frame) { + serial.set_frame(frame); + } + + public void set_remote(int in_serial, int in_channel) { + launcher_serial = in_serial; + launcher_channel = in_channel; + } + + public AltosLaunch(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException { + + device = in_device; + serial = new AltosSerial(device); + } +} \ No newline at end of file diff --git a/altosui/AltosLaunchUI.java b/altosui/AltosLaunchUI.java new file mode 100644 index 00000000..4e630afb --- /dev/null +++ b/altosui/AltosLaunchUI.java @@ -0,0 +1,518 @@ +/* + * Copyright © 2010 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import javax.swing.event.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.*; + +class FireButton extends JButton { + protected void processMouseEvent(MouseEvent e) { + super.processMouseEvent(e); + switch (e.getID()) { + case MouseEvent.MOUSE_PRESSED: + if (actionListener != null) + actionListener.actionPerformed(new ActionEvent(this, e.getID(), "fire_down")); + break; + case MouseEvent.MOUSE_RELEASED: + if (actionListener != null) + actionListener.actionPerformed(new ActionEvent(this, e.getID(), "fire_up")); + break; + } + } + + public FireButton(String s) { + super(s); + } +} + +public class AltosLaunchUI + extends JDialog + implements ActionListener +{ + AltosDevice device; + JFrame owner; + JLabel label; + + int radio_channel; + JLabel radio_channel_label; + JTextField radio_channel_text; + + int launcher_serial; + JLabel launcher_serial_label; + JTextField launcher_serial_text; + + int launcher_channel; + JLabel launcher_channel_label; + JTextField launcher_channel_text; + + JLabel armed_label; + JLabel armed_status_label; + JLabel igniter; + JLabel igniter_status_label; + JToggleButton arm; + FireButton fire; + javax.swing.Timer arm_timer; + javax.swing.Timer fire_timer; + + boolean firing; + boolean armed; + int armed_status; + int igniter_status; + int rssi; + + final static int arm_timeout = 1 * 1000; + final static int fire_timeout = 250; + + int armed_count; + + LinkedBlockingQueue command_queue; + + class LaunchHandler implements Runnable { + AltosLaunch launch; + JFrame owner; + + void send_exception(Exception e) { + final Exception f_e = e; + Runnable r = new Runnable() { + public void run() { + launch_exception(f_e); + } + }; + SwingUtilities.invokeLater(r); + } + + public void run () { + try { + launch = new AltosLaunch(device); + } catch (Exception e) { + send_exception(e); + return; + } + launch.set_frame(owner); + launch.set_remote(launcher_serial, launcher_channel); + + for (;;) { + Runnable r; + + try { + String command = command_queue.take(); + String reply = null; + + if (command.equals("get_status")) { + launch.status(); + reply = "status"; + armed_status = launch.armed; + igniter_status = launch.igniter; + rssi = launch.rssi; + } else if (command.equals("set_remote")) { + launch.set_remote(launcher_serial, launcher_channel); + reply = "remote set"; + } else if (command.equals("arm")) { + launch.arm(); + reply = "armed"; + } else if (command.equals("fire")) { + launch.fire(); + reply = "fired"; + } else if (command.equals("quit")) { + launch.close(); + break; + } else { + throw new ParseException(String.format("invalid command %s", command), 0); + } + final String f_reply = reply; + r = new Runnable() { + public void run() { + launch_reply(f_reply); + } + }; + SwingUtilities.invokeLater(r); + } catch (Exception e) { + send_exception(e); + } + } + } + + public LaunchHandler(JFrame in_owner) { + owner = in_owner; + } + } + + void launch_exception(Exception e) { + if (e instanceof FileNotFoundException) { + JOptionPane.showMessageDialog(owner, + String.format("Cannot open device \"%s\"", + device.toShortString()), + "Cannot open target device", + JOptionPane.ERROR_MESSAGE); + } else if (e instanceof AltosSerialInUseException) { + JOptionPane.showMessageDialog(owner, + String.format("Device \"%s\" already in use", + device.toShortString()), + "Device in use", + JOptionPane.ERROR_MESSAGE); + } else if (e instanceof IOException) { + IOException ee = (IOException) e; + JOptionPane.showMessageDialog(owner, + device.toShortString(), + ee.getLocalizedMessage(), + JOptionPane.ERROR_MESSAGE); + } else { + JOptionPane.showMessageDialog(owner, + String.format("Connection to \"%s\" failed", + device.toShortString()), + "Connection Failed", + JOptionPane.ERROR_MESSAGE); + } + close(); + } + + void launch_reply(String reply) { + if (reply == null) + return; + if (reply.equals("remote set")) + poll_launch_status(); + if (reply.equals("status")) { + set_launch_status(); + } + } + + void set_arm_text() { + if (arm.isSelected()) + arm.setText(String.format("%d", armed_count)); + else + arm.setText("Arm"); + } + + void start_arm_timer() { + armed_count = 30; + set_arm_text(); + } + + void stop_arm_timer() { + armed_count = 0; + armed = false; + arm.setSelected(false); + fire.setEnabled(false); + set_arm_text(); + } + + void cancel () { + fire.setEnabled(false); + firing = false; + stop_arm_timer(); + } + + void send_command(String command) { + try { + command_queue.put(command); + } catch (Exception ex) { + launch_exception(ex); + } + } + + boolean getting_status = false; + + void set_launch_status() { + getting_status = false; + armed_status_label.setText(String.format("\"%s\"", AltosLaunch.status_string(armed_status))); + igniter_status_label.setText(String.format("\"%s\"", AltosLaunch.status_string(igniter_status))); + } + + void poll_launch_status() { + if (!getting_status && !firing && !armed) { + getting_status = true; + send_command("get_status"); + } + } + + void fired() { + firing = false; + cancel(); + } + + void close() { + send_command("quit"); + arm_timer.stop(); + setVisible(false); + dispose(); + } + + void tick_arm_timer() { + if (armed_count > 0) { + --armed_count; + if (armed_count <= 0) { + armed_count = 0; + cancel(); + } else { + if (!firing) { + send_command("arm"); + set_arm_text(); + } + } + } + poll_launch_status(); + } + + void arm() { + if (arm.isSelected()) { + fire.setEnabled(true); + start_arm_timer(); + if (!firing) + send_command("arm"); + armed = true; + } else + cancel(); + } + + void fire_more() { + if (firing) + send_command("fire"); + } + + void fire_down() { + if (arm.isEnabled() && arm.isSelected() && armed_count > 0) { + firing = true; + fire_more(); + fire_timer.restart(); + } + } + + void fire_up() { + firing = false; + fire_timer.stop(); + } + + void set_radio() { + try { + radio_channel = Integer.parseInt(radio_channel_text.getText()); + } catch (NumberFormatException ne) { + radio_channel_text.setText(String.format("%d", radio_channel)); + } + } + + void set_serial() { + try { + launcher_serial = Integer.parseInt(launcher_serial_text.getText()); + AltosPreferences.set_launcher_serial(launcher_serial); + send_command("set_remote"); + } catch (NumberFormatException ne) { + launcher_serial_text.setText(String.format("%d", launcher_serial)); + } + } + + void set_channel() { + try { + launcher_channel = Integer.parseInt(launcher_channel_text.getText()); + AltosPreferences.set_launcher_serial(launcher_channel); + send_command("set_remote"); + } catch (NumberFormatException ne) { + launcher_channel_text.setText(String.format("%d", launcher_channel)); + } + } + + public void actionPerformed(ActionEvent e) { + String cmd = e.getActionCommand(); + System.out.printf("cmd %s\n", cmd); + if (cmd.equals("armed") || cmd.equals("igniter")) { + stop_arm_timer(); + } + + if (cmd.equals("arm")) + arm(); + if (cmd.equals("tick_arm")) + tick_arm_timer(); + if (cmd.equals("close")) + close(); + if (cmd.equals("fire_down")) + fire_down(); + if (cmd.equals("fire_up")) + fire_up(); + if (cmd.equals("tick_fire")) + fire_more(); + if (cmd.equals("new_serial")) + set_serial(); + if (cmd.equals("new_channel")) + set_channel(); + } + + /* A window listener to catch closing events and tell the config code */ + class ConfigListener extends WindowAdapter { + AltosLaunchUI ui; + + public ConfigListener(AltosLaunchUI this_ui) { + ui = this_ui; + } + + public void windowClosing(WindowEvent e) { + ui.actionPerformed(new ActionEvent(e.getSource(), + ActionEvent.ACTION_PERFORMED, + "close")); + } + } + + private boolean open() { + command_queue = new LinkedBlockingQueue(); + + device = AltosDeviceDialog.show(owner, Altos.product_any); + if (device != null) { + LaunchHandler handler = new LaunchHandler(owner); + Thread t = new Thread(handler); + t.start(); + return true; + } + return false; + } + + public AltosLaunchUI(JFrame in_owner) { + + launcher_channel = AltosPreferences.launcher_channel(); + launcher_serial = AltosPreferences.launcher_serial(); + owner = in_owner; + armed_status = AltosLaunch.Unknown; + igniter_status = AltosLaunch.Unknown; + + if (!open()) + return; + + Container pane = getContentPane(); + GridBagConstraints c = new GridBagConstraints(); + Insets i = new Insets(4,4,4,4); + + arm_timer = new javax.swing.Timer(arm_timeout, this); + arm_timer.setActionCommand("tick_arm"); + arm_timer.restart(); + + fire_timer = new javax.swing.Timer(fire_timeout, this); + fire_timer.setActionCommand("tick_fire"); + + owner = in_owner; + + pane.setLayout(new GridBagLayout()); + + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.insets = i; + c.weightx = 1; + c.weighty = 1; + + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 2; + c.anchor = GridBagConstraints.CENTER; + label = new JLabel ("Launch Controller"); + pane.add(label, c); + + c.gridx = 0; + c.gridy = 1; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + launcher_serial_label = new JLabel("Launcher Serial"); + pane.add(launcher_serial_label, c); + + c.gridx = 1; + c.gridy = 1; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + launcher_serial_text = new JTextField(7); + launcher_serial_text.setText(String.format("%d", launcher_serial)); + launcher_serial_text.setActionCommand("new_serial"); + launcher_serial_text.addActionListener(this); + pane.add(launcher_serial_text, c); + + c.gridx = 0; + c.gridy = 2; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + launcher_channel_label = new JLabel("Launcher Channel"); + pane.add(launcher_channel_label, c); + + c.gridx = 1; + c.gridy = 2; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + launcher_channel_text = new JTextField(7); + launcher_channel_text.setText(String.format("%d", launcher_channel)); + launcher_channel_text.setActionCommand("new_channel"); + launcher_channel_text.addActionListener(this); + pane.add(launcher_channel_text, c); + + c.gridx = 0; + c.gridy = 3; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + armed_label = new JLabel ("Armed"); + pane.add(armed_label, c); + + c.gridx = 1; + c.gridy = 3; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + armed_status_label = new JLabel(); + pane.add(armed_status_label, c); + + c.gridx = 0; + c.gridy = 4; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + igniter = new JLabel ("Igniter"); + pane.add(igniter, c); + + c.gridx = 1; + c.gridy = 4; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + igniter_status_label = new JLabel(); + pane.add(igniter_status_label, c); + + c.gridx = 0; + c.gridy = 5; + c.gridwidth = 1; + c.anchor = GridBagConstraints.CENTER; + arm = new JToggleButton ("Arm"); + pane.add(arm, c); + arm.addActionListener(this); + arm.setActionCommand("arm"); + arm.setEnabled(true); + + c.gridx = 1; + c.gridy = 5; + c.gridwidth = 1; + c.anchor = GridBagConstraints.CENTER; + fire = new FireButton ("Fire"); + fire.setEnabled(false); + pane.add(fire, c); + fire.addActionListener(this); + fire.setActionCommand("fire"); + + pack(); + setLocationRelativeTo(owner); + + addWindowListener(new ConfigListener(this)); + + setVisible(true); + } +} \ No newline at end of file diff --git a/altosui/AltosPreferences.java b/altosui/AltosPreferences.java index 716559ab..48aed441 100644 --- a/altosui/AltosPreferences.java +++ b/altosui/AltosPreferences.java @@ -58,6 +58,12 @@ class AltosPreferences { /* font size preferences name */ final static String fontSizePreference = "FONT-SIZE"; + /* Launcher serial preference name */ + final static String launcherSerialPreference = "LAUNCHER-SERIAL"; + + /* Launcher channel prefernce name */ + final static String launcherChannelPreference = "LAUNCHER-CHANNEL"; + /* Default logdir is ~/TeleMetrum */ final static String logdirName = "TeleMetrum"; @@ -143,6 +149,9 @@ class AltosPreferences { node.put(String.format(description_format, i), frequencies[i].description); } } + static int launcher_serial; + + static int launcher_channel; public static void init() { preferences = Preferences.userRoot().node("/org/altusmetrum/altosui"); @@ -176,6 +185,10 @@ class AltosPreferences { font_size = preferences.getInt(fontSizePreference, Altos.font_size_medium); Altos.set_fonts(font_size); + launcher_serial = preferences.getInt(launcherSerialPreference, 0); + + launcher_channel = preferences.getInt(launcherChannelPreference, 0); + String firmwaredir_string = preferences.get(firmwaredirPreference, null); if (firmwaredir_string != null) firmwaredir = new File(firmwaredir_string); @@ -390,6 +403,32 @@ class AltosPreferences { return serial_debug; } + public static void set_launcher_serial(int new_launcher_serial) { + launcher_serial = new_launcher_serial; + System.out.printf("set launcher serial to %d\n", new_launcher_serial); + synchronized (preferences) { + preferences.putInt(launcherSerialPreference, launcher_serial); + flush_preferences(); + } + } + + public static int launcher_serial() { + return launcher_serial; + } + + public static void set_launcher_channel(int new_launcher_channel) { + launcher_channel = new_launcher_channel; + System.out.printf("set launcher channel to %d\n", new_launcher_channel); + synchronized (preferences) { + preferences.putInt(launcherChannelPreference, launcher_channel); + flush_preferences(); + } + } + + public static int launcher_channel() { + return launcher_channel; + } + public static Preferences bt_devices() { return preferences.node("bt_devices"); } diff --git a/altosui/Makefile.am b/altosui/Makefile.am index ba1c830c..e2e42d84 100644 --- a/altosui/Makefile.am +++ b/altosui/Makefile.am @@ -68,6 +68,8 @@ altosui_JAVA = \ AltosIdleMonitorUI.java \ AltosIgnite.java \ AltosIgniteUI.java \ + AltosLaunch.java \ + AltosLaunchUI.java \ AltosInfoTable.java \ AltosKML.java \ AltosLanded.java \ diff --git a/src/ao_radio_cmac.c b/src/ao_radio_cmac.c index c2757b16..41fbbe1f 100644 --- a/src/ao_radio_cmac.c +++ b/src/ao_radio_cmac.c @@ -21,6 +21,7 @@ #define AO_CMAC_MAX_LEN (128 - AO_CMAC_KEY_LEN) static __xdata uint8_t ao_radio_cmac_mutex; +__pdata int16_t ao_radio_cmac_rssi; static __xdata uint8_t cmac_data[AO_CMAC_MAX_LEN + AO_CMAC_KEY_LEN + 2 + AO_CMAC_KEY_LEN]; static __pdata uint8_t ao_radio_cmac_len; @@ -114,9 +115,12 @@ radio_cmac_recv(uint8_t len, uint16_t timeout) __reentrant i = ao_radio_recv(cmac_data, len + AO_CMAC_KEY_LEN + 2); ao_clear_alarm(); - if (!i) + if (!i) { + ao_radio_cmac_rssi = 0; return AO_RADIO_CMAC_TIMEOUT; + } + ao_radio_cmac_rssi = (int16_t) (((int8_t) cmac_data[len + AO_CMAC_KEY_LEN]) >> 1) - 74; if (!(cmac_data[len + AO_CMAC_KEY_LEN +1] & PKT_APPEND_STATUS_1_CRC_OK)) return AO_RADIO_CMAC_CRC_ERROR; @@ -221,33 +225,46 @@ radio_cmac_recv_cmd(void) __reentrant printf ("PACKET "); for (i = 0; i < len; i++) printf("%02x", cmac_data[i]); - printf ("\n"); + printf (" %d\n", ao_radio_cmac_rssi); } else - printf ("ERROR %d\n", i); + printf ("ERROR %d %d\n", i, ao_radio_cmac_rssi); ao_mutex_put(&ao_radio_cmac_mutex); } static __xdata struct ao_launch_command command; static __xdata struct ao_launch_query query; +static pdata uint16_t launch_serial; +static pdata uint8_t launch_channel; +static pdata uint16_t tick_offset; +static void +launch_args(void) __reentrant +{ + ao_cmd_decimal(); + launch_serial = ao_cmd_lex_i; + ao_cmd_decimal(); + launch_channel = ao_cmd_lex_i; +} static int8_t -launch_query(uint16_t serial, uint8_t channel) +launch_query(void) { uint8_t i; int8_t r = AO_RADIO_CMAC_OK; + tick_offset = ao_time(); for (i = 0; i < 10; i++) { printf ("."); flush(); command.tick = ao_time(); - command.serial = serial; + command.serial = launch_serial; command.cmd = AO_LAUNCH_QUERY; - command.channel = channel; + command.channel = launch_channel; ao_radio_cmac_send(&command, sizeof (command)); r = ao_radio_cmac_recv(&query, sizeof (query), AO_MS_TO_TICKS(500)); if (r == AO_RADIO_CMAC_OK) break; } + tick_offset -= query.tick; printf("\n"); flush(); return r; } @@ -255,17 +272,12 @@ launch_query(uint16_t serial, uint8_t channel) static void launch_report_cmd(void) __reentrant { - uint8_t channel; - uint16_t serial; int8_t r; - ao_cmd_decimal(); - serial = ao_cmd_lex_i; - ao_cmd_decimal(); - channel = ao_cmd_lex_i; + launch_args(); if (ao_cmd_status != ao_cmd_success) return; - r = launch_query(serial, channel); + r = launch_query(); switch (r) { case AO_RADIO_CMAC_OK: if (query.valid) { @@ -273,24 +285,25 @@ launch_report_cmd(void) __reentrant case ao_igniter_ready: case ao_igniter_active: printf ("Armed: "); - switch (query.igniter_status) { - default: - printf("unknown status\n"); - break; - case ao_igniter_ready: - printf("igniter good\n"); - break; - case ao_igniter_open: - printf("igniter bad\n"); - break; - } break; default: - printf("Disarmed\n"); + printf("Disarmed: "); + } + switch (query.igniter_status) { + default: + printf("unknown\n"); + break; + case ao_igniter_ready: + printf("igniter good\n"); + break; + case ao_igniter_open: + printf("igniter bad\n"); + break; } } else { - printf("Invalid channel %d\n", channel); + printf("Invalid channel %d\n", launch_channel); } + printf("Rssi: %d\n", ao_radio_cmac_rssi); break; default: printf("Error %d\n", r); @@ -298,56 +311,91 @@ launch_report_cmd(void) __reentrant } } +static void +launch_arm(void) __reentrant +{ + command.tick = ao_time() - tick_offset; + command.serial = launch_serial; + command.cmd = AO_LAUNCH_ARM; + command.channel = launch_channel; + ao_radio_cmac_send(&command, sizeof (command)); +} + +static void +launch_ignite(void) __reentrant +{ + command.tick = ao_time() - tick_offset; + command.serial = launch_serial; + command.cmd = AO_LAUNCH_FIRE; + command.channel = 0; + ao_radio_cmac_send(&command, sizeof (command)); +} + static void launch_fire_cmd(void) __reentrant { static __xdata struct ao_launch_command command; - uint8_t channel; - uint16_t serial; uint8_t secs; uint8_t i; int8_t r; - uint16_t tick_offset; - ao_cmd_decimal(); - serial = ao_cmd_lex_i; - ao_cmd_decimal(); - channel = ao_cmd_lex_i; + launch_args(); ao_cmd_decimal(); secs = ao_cmd_lex_i; if (ao_cmd_status != ao_cmd_success) return; - tick_offset = ao_time(); - r = launch_query(serial, channel); - tick_offset -= query.tick; + r = launch_query(); + if (r != AO_RADIO_CMAC_OK) { + printf("query failed %d\n", r); + return; + } for (i = 0; i < 4; i++) { printf("arm %d\n", i); flush(); - command.tick = ao_time() - tick_offset; - command.serial = serial; - command.cmd = AO_LAUNCH_ARM; - command.channel = channel; - ao_radio_cmac_send(&command, sizeof (command)); + launch_arm(); } + secs = secs * 10 - 5; if (secs > 100) secs = 100; for (i = 0; i < secs; i++) { printf("fire %d\n", i); flush(); - command.tick = ao_time() - tick_offset; - command.serial = serial; - command.cmd = AO_LAUNCH_FIRE; - command.channel = 0; - ao_radio_cmac_send(&command, sizeof (command)); + launch_ignite(); ao_delay(AO_MS_TO_TICKS(100)); } } +static void +launch_arm_cmd(void) __reentrant +{ + uint8_t i; + int8_t r; + launch_args(); + r = launch_query(); + if (r != AO_RADIO_CMAC_OK) { + printf("query failed %d\n", r); + return; + } + for (i = 0; i < 4; i++) + launch_arm(); +} + +static void +launch_ignite_cmd(void) __reentrant +{ + uint8_t i; + launch_args(); + for (i = 0; i < 4; i++) + launch_ignite(); +} + static __code struct ao_cmds ao_radio_cmac_cmds[] = { { radio_cmac_send_cmd, "s \0Send AES-CMAC packet. Bytes to send follow on next line" }, { radio_cmac_recv_cmd, "S \0Receive AES-CMAC packet. Timeout in ms" }, { launch_report_cmd, "l \0Get remote launch status" }, { launch_fire_cmd, "f \0Fire remote igniter" }, + { launch_arm_cmd, "a \0Arm remote igniter" }, + { launch_ignite_cmd, "i \0Pulse remote igniter" }, { 0, NULL }, }; -- 2.30.2