diff --git a/app/src/cc/arduino/view/SplashScreenHelper.java b/app/src/cc/arduino/view/SplashScreenHelper.java index d9f5fedb632..7d5948fec8b 100644 --- a/app/src/cc/arduino/view/SplashScreenHelper.java +++ b/app/src/cc/arduino/view/SplashScreenHelper.java @@ -89,7 +89,7 @@ public void close() { splash.close(); } - public void printText(String str) { + private void printText(String str) { System.out.println(str); } diff --git a/app/src/cc/arduino/view/StubMenuListener.java b/app/src/cc/arduino/view/StubMenuListener.java index 7d2cfe591dd..dd9654b8295 100644 --- a/app/src/cc/arduino/view/StubMenuListener.java +++ b/app/src/cc/arduino/view/StubMenuListener.java @@ -1,3 +1,32 @@ +/* + * This file is part of Arduino. + * + * Copyright 2015 Arduino LLC (http://www.arduino.cc/) + * + * Arduino 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + */ + package cc.arduino.view; import javax.swing.event.MenuEvent; diff --git a/app/src/cc/arduino/view/preferences/AdditionalBoardsManagerURLTextArea.java b/app/src/cc/arduino/view/preferences/AdditionalBoardsManagerURLTextArea.java index e09574e9bef..a75e1c8b43a 100644 --- a/app/src/cc/arduino/view/preferences/AdditionalBoardsManagerURLTextArea.java +++ b/app/src/cc/arduino/view/preferences/AdditionalBoardsManagerURLTextArea.java @@ -35,6 +35,7 @@ import com.google.common.collect.FluentIterable; import processing.app.Base; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; @@ -47,7 +48,7 @@ public class AdditionalBoardsManagerURLTextArea extends javax.swing.JDialog { private ActionListener onOkListener; - public AdditionalBoardsManagerURLTextArea(java.awt.Frame parent) { + public AdditionalBoardsManagerURLTextArea(Window parent) { super(parent); initComponents(); setLocationRelativeTo(parent); diff --git a/app/src/cc/arduino/view/preferences/Preferences.form b/app/src/cc/arduino/view/preferences/Preferences.form new file mode 100644 index 00000000000..8286450180d --- /dev/null +++ b/app/src/cc/arduino/view/preferences/Preferences.form @@ -0,0 +1,634 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/app/src/cc/arduino/view/preferences/Preferences.java b/app/src/cc/arduino/view/preferences/Preferences.java new file mode 100644 index 00000000000..2d026297013 --- /dev/null +++ b/app/src/cc/arduino/view/preferences/Preferences.java @@ -0,0 +1,770 @@ +/* + * This file is part of Arduino. + * + * Copyright 2015 Arduino LLC (http://www.arduino.cc/) + * + * Arduino 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + */ + +package cc.arduino.view.preferences; + +import processing.app.Base; +import processing.app.Editor; +import processing.app.I18n; +import processing.app.PreferencesData; +import processing.app.helpers.FileUtils; +import processing.app.helpers.OSUtils; +import processing.app.legacy.PApplet; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.io.File; + +import static processing.app.I18n._; + +public class Preferences extends javax.swing.JDialog { + + private final Language[] languages; + private final Language[] missingLanguages; + private final WarningItem[] warningItems; + private final Base base; + + public static class Language { + + private final String name; + private final String originalName; + private final String isoCode; + + public Language(String name, String originalName, String isoCode) { + this.name = name; + this.originalName = originalName; + this.isoCode = isoCode; + } + + public String toString() { + if (originalName.length() == 0) { + return name; + } + return originalName + " (" + name + ")"; + } + + public String getIsoCode() { + return isoCode; + } + } + + private static class WarningItem { + private final String value; + private final String translation; + + public WarningItem(String value, String translation) { + this.value = value; + this.translation = translation; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return translation; + } + } + + public Preferences(Window parent, Base base) { + super(parent); + this.base = base; + + this.languages = new Language[]{ + new Language(_("System Default"), "", ""), + new Language(_("Albanian"), "shqip", "sq"), + new Language(_("Arabic"), "العربية", "ar"), + new Language(_("Aragonese"), "Aragonés", "an"), + new Language(_("Belarusian"), "Беларуская мова", "be"), + new Language(_("Bulgarian"), "български", "bg"), + new Language(_("Catalan"), "Català", "ca"), + new Language(_("Chinese Simplified"), "简体中文", "zh_CN"), + new Language(_("Chinese Traditional"), "繁體中文", "zh_TW"), + new Language(_("Croatian"), "Hrvatski", "hr_HR"), + new Language(_("Czech (Czech Republic)"), "český (Czech Republic)", "cs_CZ"), + new Language(_("Danish (Denmark)"), "Dansk (Denmark)", "da_DK"), + new Language(_("Dutch"), "Nederlands", "nl"), + new Language(_("English"), "English", "en"), + new Language(_("English (United Kingdom)"), "English (United Kingdom)", "en_GB"), + new Language(_("Estonian"), "Eesti", "et"), + new Language(_("Estonian (Estonia)"), "Eesti keel", "et_EE"), + new Language(_("Filipino"), "Pilipino", "fil"), + new Language(_("Finnish"), "Suomi", "fi"), + new Language(_("French"), "Français", "fr"), + new Language(_("Canadian French"), "Canadienne-français", "fr_CA"), + new Language(_("Galician"), "Galego", "gl"), + new Language(_("Georgian"), "საქართველოს", "ka_GE"), + new Language(_("German"), "Deutsch", "de_DE"), + new Language(_("Greek"), "ελληνικά", "el_GR"), + new Language(_("Hebrew"), "עברית", "he"), + new Language(_("Hindi"), "हिंदी", "hi"), + new Language(_("Hungarian"), "Magyar", "hu"), + new Language(_("Indonesian"), "Bahasa Indonesia", "id"), + new Language(_("Italian"), "Italiano", "it_IT"), + new Language(_("Japanese"), "日本語", "ja_JP"), + new Language(_("Korean"), "한국어", "ko_KR"), + new Language(_("Latvian"), "Latviešu", "lv_LV"), + new Language(_("Lithuaninan"), "Lietuvių Kalba", "lt_LT"), + new Language(_("Norwegian Bokmål"), "Norsk bokmål", "nb_NO"), + new Language(_("Persian"), "فارسی", "fa"), + new Language(_("Polish"), "Język Polski", "pl"), + new Language(_("Portuguese (Brazil)"), "Português (Brazil)", "pt_BR"), + new Language(_("Portuguese (Portugal)"), "Português (Portugal)", "pt_PT"), + new Language(_("Romanian"), "Română", "ro"), + new Language(_("Russian"), "Русский", "ru"), + new Language(_("Slovenian"), "Slovenščina", "sl_SI"), + new Language(_("Spanish"), "Español", "es"), + new Language(_("Swedish"), "Svenska", "sv"), + new Language(_("Tamil"), "தமிழ்", "ta"), + new Language(_("Turkish"), "Türk", "tr"), + new Language(_("Ukrainian"), "Український", "uk"), + new Language(_("Vietnamese"), "Tiếng Việt", "vi"), + }; + + this.missingLanguages = new Language[]{ + new Language(_("Afrikaans"), "Afrikaans", "af"), + new Language(_("Armenian"), "Հայերեն", "hy"), + new Language(_("Asturian"), "Asturianu", "ast"), + new Language(_("Basque"), "Euskara", "eu"), + new Language(_("Bengali (India)"), "বাংলা (India)", "bn_IN"), + new Language(_("Bosnian"), "Bosanski", "bs"), + new Language(_("Burmese (Myanmar)"), "ဗမာစကား", "my_MM"), + new Language(_("Chinese (China)"), "", "zh_CN"), + new Language(_("Chinese (Hong Kong)"), "", "zh_HK"), + new Language(_("Chinese (Taiwan)"), "", "zh_TW"), + new Language(_("Chinese (Taiwan) (Big5)"), "", "zh_TW.Big5"), + new Language(_("Czech"), "český", "cs"), + new Language(_("Danish"), "Dansk", "da"), + new Language(_("Dutch (Netherlands)"), "Nederlands", "nl_NL"), + new Language(_("Galician (Spain)"), "Galego (Spain)", "gl_ES"), + new Language(_("Nepali"), "नेपाली", "ne"), + new Language(_("N'Ko"), "ߒߞߏ", "nqo"), + new Language(_("Marathi"), "मराठी", "mr"), + new Language(_("Malay (Malaysia)"), "بهاس ملايو (Malaysia)", "ms_MY"), + new Language(_("Norwegian"), "Norsk", "no"), + new Language(_("Norwegian Nynorsk"), "Norsk Nynorsk", "nn"), + new Language(_("Portugese"), "Português", "pt"), + new Language(_("Persian (Iran)"), "فارسی (Iran)", "fa_IR"), + new Language(_("Slovak"), "Slovenčina", "sk"), + new Language(_("Swahili"), "كِسوَهِل", "sw"), + new Language(_("Talossan"), "Talossan", "tzl"), + new Language(_("Urdu (Pakistan)"), "اردو (Pakistan)", "ur_PK"), + new Language(_("Western Frisian"), "Western Frisian", "fy"), + }; + + this.warningItems = new WarningItem[]{ + new WarningItem("none", _("None")), + new WarningItem("default", _("Default")), + new WarningItem("more", _("More")), + new WarningItem("all", _("All")) + }; + + initComponents(); + + Base.registerWindowCloseKeys(getRootPane(), new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + cancelButtonActionPerformed(e); + } + }); + + if (!OSUtils.isWindows() || base.getPortableFolder() != null) { + autoAssociateBox.setEnabled(false); + autoAssociateBox.getParent().remove(autoAssociateBox); + } + + showPrerefencesData(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + javax.swing.JLabel sketchbookLocationLabel = new javax.swing.JLabel(); + sketchbookLocationField = new javax.swing.JTextField(); + javax.swing.JButton browseButton = new javax.swing.JButton(); + javax.swing.JLabel comboLanguageLabel = new javax.swing.JLabel(); + comboLanguage = new JComboBox(languages); + javax.swing.JLabel requiresRestartLabel = new javax.swing.JLabel(); + javax.swing.JLabel fontSizeLabel = new javax.swing.JLabel(); + fontSizeField = new javax.swing.JTextField(); + javax.swing.JLabel showVerboseLabel = new javax.swing.JLabel(); + verboseCompilationBox = new javax.swing.JCheckBox(); + verboseUploadBox = new javax.swing.JCheckBox(); + javax.swing.JLabel comboWarningsLabel = new javax.swing.JLabel(); + comboWarnings = new JComboBox(warningItems); + javax.swing.JPanel proxySettingsPanel = new javax.swing.JPanel(); + javax.swing.JLabel proxyHTTPServerLabel = new javax.swing.JLabel(); + proxyHTTPServer = new javax.swing.JTextField(); + javax.swing.JLabel proxyHTTPPortLabel = new javax.swing.JLabel(); + proxyHTTPPort = new javax.swing.JTextField(); + javax.swing.JLabel proxyHTTPSServerLabel = new javax.swing.JLabel(); + proxyHTTPSServer = new javax.swing.JTextField(); + javax.swing.JLabel proxyHTTPSPortLabel = new javax.swing.JLabel(); + proxyHTTPSPort = new javax.swing.JTextField(); + javax.swing.JLabel proxyUserLabel = new javax.swing.JLabel(); + proxyUser = new javax.swing.JTextField(); + javax.swing.JLabel proxyPasswordLabel = new javax.swing.JLabel(); + proxyPassword = new javax.swing.JPasswordField(); + javax.swing.JLabel additionalBoardsManagerLabel = new javax.swing.JLabel(); + additionalBoardsManagerField = new javax.swing.JTextField(); + javax.swing.JButton extendedAdditionalUrlFieldWindow = new javax.swing.JButton(); + javax.swing.JLabel morePreferencesLabel = new javax.swing.JLabel(); + preferencesFileLabel = new javax.swing.JLabel(); + javax.swing.JLabel arduinoNotRunningLabel = new javax.swing.JLabel(); + javax.swing.JButton okButton = new javax.swing.JButton(); + javax.swing.JButton cancelButton = new javax.swing.JButton(); + javax.swing.JPanel checkboxesContainer = new javax.swing.JPanel(); + displayLineNumbersBox = new javax.swing.JCheckBox(); + enableCodeFoldingBox = new javax.swing.JCheckBox(); + verifyUploadBox = new javax.swing.JCheckBox(); + externalEditorBox = new javax.swing.JCheckBox(); + checkUpdatesBox = new javax.swing.JCheckBox(); + updateExtensionBox = new javax.swing.JCheckBox(); + autoAssociateBox = new javax.swing.JCheckBox(); + saveVerifyUploadBox = new javax.swing.JCheckBox(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle(_("Preferences")); + setModal(true); + setResizable(false); + + sketchbookLocationLabel.setText(_("Sketchbook location:")); + + sketchbookLocationField.setColumns(40); + + browseButton.setText(I18n.PROMPT_BROWSE); + browseButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + browseButtonActionPerformed(evt); + } + }); + + comboLanguageLabel.setText(_("Editor language: ")); + + requiresRestartLabel.setText(_(" (requires restart of Arduino)")); + + fontSizeLabel.setText(_("Editor font size: ")); + + fontSizeField.setColumns(4); + + showVerboseLabel.setText(_("Show verbose output during: ")); + + verboseCompilationBox.setText(_("compilation ")); + + verboseUploadBox.setText(_("upload")); + + comboWarningsLabel.setText(_("Compiler warnings: ")); + + proxySettingsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(_("Proxy Settings"))); + + proxyHTTPServerLabel.setText(_("Server (HTTP):")); + + proxyHTTPServer.setColumns(10); + + proxyHTTPPortLabel.setText(_("Port (HTTP):")); + + proxyHTTPPort.setColumns(10); + + proxyHTTPSServerLabel.setText(_("Server (HTTPS):")); + + proxyHTTPSServer.setColumns(10); + + proxyHTTPSPortLabel.setText(_("Port (HTTPS):")); + + proxyHTTPSPort.setColumns(10); + + proxyUserLabel.setText(_("Username:")); + + proxyUser.setColumns(10); + + proxyPasswordLabel.setText(_("Password:")); + + proxyPassword.setColumns(10); + + javax.swing.GroupLayout proxySettingsPanelLayout = new javax.swing.GroupLayout(proxySettingsPanel); + proxySettingsPanel.setLayout(proxySettingsPanelLayout); + proxySettingsPanelLayout.setHorizontalGroup( + proxySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(proxySettingsPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(proxySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(proxySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(proxyHTTPSServerLabel) + .addGroup(proxySettingsPanelLayout.createSequentialGroup() + .addGap(8, 8, 8) + .addComponent(proxyHTTPServerLabel))) + .addComponent(proxyUserLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(proxySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(proxyHTTPServer) + .addComponent(proxyHTTPSServer) + .addComponent(proxyUser, javax.swing.GroupLayout.Alignment.TRAILING)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(proxySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(proxyHTTPSPortLabel) + .addComponent(proxyPasswordLabel, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(proxyHTTPPortLabel, javax.swing.GroupLayout.Alignment.TRAILING)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(proxySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(proxyHTTPSPort, javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(proxyHTTPPort, javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(proxyPassword, javax.swing.GroupLayout.Alignment.LEADING)) + .addContainerGap()) + ); + proxySettingsPanelLayout.setVerticalGroup( + proxySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(proxySettingsPanelLayout.createSequentialGroup() + .addGroup(proxySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(proxySettingsPanelLayout.createSequentialGroup() + .addGroup(proxySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(proxyHTTPServerLabel) + .addComponent(proxyHTTPServer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(proxySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(proxyHTTPSServerLabel) + .addComponent(proxyHTTPSServer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(proxySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(proxyUser, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(proxyUserLabel))) + .addGroup(proxySettingsPanelLayout.createSequentialGroup() + .addGroup(proxySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(proxyHTTPPortLabel) + .addComponent(proxyHTTPPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(proxySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(proxyHTTPSPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(proxyHTTPSPortLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(proxySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(proxyPasswordLabel) + .addComponent(proxyPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + additionalBoardsManagerLabel.setText(_("Additional Boards Manager URLs: ")); + + extendedAdditionalUrlFieldWindow.setIcon(new ImageIcon(Base.getThemeImage("newwindow.gif", this))); + extendedAdditionalUrlFieldWindow.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + extendedAdditionalUrlFieldWindowActionPerformed(evt); + } + }); + + morePreferencesLabel.setForeground(Color.GRAY); + morePreferencesLabel.setText(_("More preferences can be edited directly in the file")); + + preferencesFileLabel.setText(PreferencesData.getPreferencesFile().getAbsolutePath()); + preferencesFileLabel.addMouseListener(new java.awt.event.MouseAdapter() { + public void mousePressed(java.awt.event.MouseEvent evt) { + preferencesFileLabelMousePressed(evt); + } + + public void mouseExited(java.awt.event.MouseEvent evt) { + preferencesFileLabelMouseExited(evt); + } + + public void mouseEntered(java.awt.event.MouseEvent evt) { + preferencesFileLabelMouseEntered(evt); + } + }); + + arduinoNotRunningLabel.setForeground(Color.GRAY); + arduinoNotRunningLabel.setText(_("(edit only when Arduino is not running)")); + + okButton.setText(I18n.PROMPT_OK); + okButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + okButtonActionPerformed(evt); + } + }); + + cancelButton.setText(I18n.PROMPT_CANCEL); + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + checkboxesContainer.setLayout(new javax.swing.BoxLayout(checkboxesContainer, javax.swing.BoxLayout.Y_AXIS)); + + displayLineNumbersBox.setText(_("Display line numbers")); + checkboxesContainer.add(displayLineNumbersBox); + + enableCodeFoldingBox.setText(_("Enable Code Folding")); + checkboxesContainer.add(enableCodeFoldingBox); + + verifyUploadBox.setText(_("Verify code after upload")); + checkboxesContainer.add(verifyUploadBox); + + externalEditorBox.setText(_("Use external editor")); + checkboxesContainer.add(externalEditorBox); + + checkUpdatesBox.setText(_("Check for updates on startup")); + checkboxesContainer.add(checkUpdatesBox); + + updateExtensionBox.setText(_("Update sketch files to new extension on save (.pde -> .ino)")); + checkboxesContainer.add(updateExtensionBox); + + autoAssociateBox.setText(_("Automatically associate .ino files with Arduino")); + checkboxesContainer.add(autoAssociateBox); + + saveVerifyUploadBox.setText(_("Save when verifying or uploading")); + checkboxesContainer.add(saveVerifyUploadBox); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(proxySettingsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(sketchbookLocationField) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(browseButton)) + .addComponent(checkboxesContainer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(arduinoNotRunningLabel) + .addComponent(sketchbookLocationLabel) + .addGroup(layout.createSequentialGroup() + .addComponent(comboWarningsLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(comboWarnings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(morePreferencesLabel) + .addGroup(layout.createSequentialGroup() + .addComponent(showVerboseLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(verboseCompilationBox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(verboseUploadBox)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(comboLanguageLabel) + .addComponent(fontSizeLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(fontSizeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(comboLanguage, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(requiresRestartLabel)))) + .addComponent(preferencesFileLabel)) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(additionalBoardsManagerLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(additionalBoardsManagerField, javax.swing.GroupLayout.PREFERRED_SIZE, 494, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(extendedAdditionalUrlFieldWindow, javax.swing.GroupLayout.PREFERRED_SIZE, 36, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(okButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton))))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addComponent(sketchbookLocationLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(sketchbookLocationField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(browseButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(comboLanguageLabel) + .addComponent(comboLanguage, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(requiresRestartLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(fontSizeLabel) + .addComponent(fontSizeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(showVerboseLabel) + .addComponent(verboseCompilationBox) + .addComponent(verboseUploadBox)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(comboWarningsLabel) + .addComponent(comboWarnings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(checkboxesContainer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(proxySettingsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(additionalBoardsManagerLabel) + .addComponent(additionalBoardsManagerField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(extendedAdditionalUrlFieldWindow, javax.swing.GroupLayout.PREFERRED_SIZE, 27, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(morePreferencesLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(preferencesFileLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(arduinoNotRunningLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(cancelButton) + .addComponent(okButton)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + pack(); + }// //GEN-END:initComponents + + private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed + File dflt = new File(sketchbookLocationField.getText()); + File file = Base.selectFolder(_("Select new sketchbook location"), dflt, this); + if (file != null) { + String path = file.getAbsolutePath(); + if (Base.getPortableFolder() != null) { + path = FileUtils.relativePath(Base.getPortableFolder().toString(), path); + if (path == null) { + path = Base.getPortableSketchbookFolder(); + } + } + sketchbookLocationField.setText(path); + } + }//GEN-LAST:event_browseButtonActionPerformed + + private void extendedAdditionalUrlFieldWindowActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_extendedAdditionalUrlFieldWindowActionPerformed + final AdditionalBoardsManagerURLTextArea additionalBoardsManagerURLTextArea = new AdditionalBoardsManagerURLTextArea(this); + additionalBoardsManagerURLTextArea.setText(additionalBoardsManagerField.getText()); + additionalBoardsManagerURLTextArea.onOk(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + additionalBoardsManagerField.setText(additionalBoardsManagerURLTextArea.getText()); + } + }); + additionalBoardsManagerURLTextArea.setVisible(true); + }//GEN-LAST:event_extendedAdditionalUrlFieldWindowActionPerformed + + private void preferencesFileLabelMouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_preferencesFileLabelMouseEntered + preferencesFileLabel.setForeground(new Color(0, 0, 140)); + }//GEN-LAST:event_preferencesFileLabelMouseEntered + + private void preferencesFileLabelMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_preferencesFileLabelMousePressed + Base.openFolder(PreferencesData.getPreferencesFile().getParentFile()); + }//GEN-LAST:event_preferencesFileLabelMousePressed + + private void preferencesFileLabelMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_preferencesFileLabelMouseExited + preferencesFileLabel.setForeground(Color.BLACK); + }//GEN-LAST:event_preferencesFileLabelMouseExited + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING)); + }//GEN-LAST:event_cancelButtonActionPerformed + + private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed + savePreferencesData(); + for (Editor editor : base.getEditors()) { + editor.applyPreferences(); + } + cancelButtonActionPerformed(evt); + }//GEN-LAST:event_okButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTextField additionalBoardsManagerField; + private javax.swing.JCheckBox autoAssociateBox; + private javax.swing.JCheckBox checkUpdatesBox; + private javax.swing.JComboBox comboLanguage; + private javax.swing.JComboBox comboWarnings; + private javax.swing.JCheckBox displayLineNumbersBox; + private javax.swing.JCheckBox enableCodeFoldingBox; + private javax.swing.JCheckBox externalEditorBox; + private javax.swing.JTextField fontSizeField; + private javax.swing.JLabel preferencesFileLabel; + private javax.swing.JTextField proxyHTTPPort; + private javax.swing.JTextField proxyHTTPSPort; + private javax.swing.JTextField proxyHTTPSServer; + private javax.swing.JTextField proxyHTTPServer; + private javax.swing.JPasswordField proxyPassword; + private javax.swing.JTextField proxyUser; + private javax.swing.JCheckBox saveVerifyUploadBox; + private javax.swing.JTextField sketchbookLocationField; + private javax.swing.JCheckBox updateExtensionBox; + private javax.swing.JCheckBox verboseCompilationBox; + private javax.swing.JCheckBox verboseUploadBox; + private javax.swing.JCheckBox verifyUploadBox; + // End of variables declaration//GEN-END:variables + + private void savePreferencesData() { + String oldPath = PreferencesData.get("sketchbook.path"); + String newPath = sketchbookLocationField.getText(); + if (newPath.isEmpty()) { + if (base.getPortableFolder() == null) { + newPath = base.getDefaultSketchbookFolderOrPromptForIt().toString(); + } else { + newPath = base.getPortableSketchbookFolder(); + } + } + if (!newPath.equals(oldPath)) { + base.rebuildSketchbookMenus(); + PreferencesData.set("sketchbook.path", newPath); + } + + Language newLanguage = (Language) comboLanguage.getSelectedItem(); + PreferencesData.set("editor.languages.current", newLanguage.getIsoCode()); + + String newSizeText = fontSizeField.getText(); + try { + int newSize = Integer.parseInt(newSizeText.trim()); + String pieces[] = PApplet.split(PreferencesData.get("editor.font"), ','); + pieces[2] = String.valueOf(newSize); + PreferencesData.set("editor.font", PApplet.join(pieces, ',')); + + } catch (Exception e) { + System.err.println(I18n.format(_("ignoring invalid font size {0}"), newSizeText)); + } + + // put each of the settings into the table + PreferencesData.setBoolean("build.verbose", verboseCompilationBox.isSelected()); + PreferencesData.setBoolean("upload.verbose", verboseUploadBox.isSelected()); + + WarningItem warningItem = (WarningItem) comboWarnings.getSelectedItem(); + PreferencesData.set("compiler.warning_level", warningItem.getValue()); + + PreferencesData.setBoolean("editor.linenumbers", displayLineNumbersBox.isSelected()); + + PreferencesData.setBoolean("editor.code_folding", enableCodeFoldingBox.isSelected()); + + PreferencesData.setBoolean("upload.verify", verifyUploadBox.isSelected()); + + PreferencesData.setBoolean("editor.save_on_verify", saveVerifyUploadBox.isSelected()); + + PreferencesData.setBoolean("editor.external", externalEditorBox.isSelected()); + + PreferencesData.setBoolean("update.check", checkUpdatesBox.isSelected()); + + PreferencesData.setBoolean("editor.update_extension", updateExtensionBox.isSelected()); + + if (autoAssociateBox != null) { + PreferencesData.setBoolean("platform.auto_file_type_associations", autoAssociateBox.isSelected()); + } + + PreferencesData.setBoolean("editor.save_on_verify", saveVerifyUploadBox.isSelected()); + + PreferencesData.set("proxy.http.server", proxyHTTPServer.getText()); + try { + PreferencesData.set("proxy.http.port", Integer.valueOf(proxyHTTPPort.getText()).toString()); + } catch (NumberFormatException e) { + PreferencesData.remove("proxy.http.port"); + } + PreferencesData.set("proxy.https.server", proxyHTTPSServer.getText()); + try { + PreferencesData.set("proxy.https.port", Integer.valueOf(proxyHTTPSPort.getText()).toString()); + } catch (NumberFormatException e) { + PreferencesData.remove("proxy.https.port"); + } + PreferencesData.set("proxy.user", proxyUser.getText()); + PreferencesData.set("proxy.password", new String(proxyPassword.getPassword())); + + PreferencesData.set("boardsmanager.additional.urls", additionalBoardsManagerField.getText().replace("\r\n", "\n").replace("\r", "\n").replace("\n", ",")); + + //editor.applyPreferences(); + } + + private void showPrerefencesData() { + sketchbookLocationField.setText(PreferencesData.get("sketchbook.path")); + + String currentLanguageISOCode = PreferencesData.get("editor.languages.current"); + for (Language language : languages) { + if (language.getIsoCode().equals(currentLanguageISOCode)) { + comboLanguage.setSelectedItem(language); + } + } + + Font editorFont = PreferencesData.getFont("editor.font"); + fontSizeField.setText(String.valueOf(editorFont.getSize())); + + verboseCompilationBox.setSelected(PreferencesData.getBoolean("build.verbose")); + verboseUploadBox.setSelected(PreferencesData.getBoolean("upload.verbose")); + + String currentWarningLevel = PreferencesData.get("compiler.warning_level", "none"); + for (WarningItem item : warningItems) { + if (currentWarningLevel.equals(item.getValue())) { + comboWarnings.setSelectedItem(item); + } + } + + displayLineNumbersBox.setSelected(PreferencesData.getBoolean("editor.linenumbers")); + + enableCodeFoldingBox.setSelected(PreferencesData.getBoolean("editor.code_folding")); + + verifyUploadBox.setSelected(PreferencesData.getBoolean("upload.verify")); + + externalEditorBox.setSelected(PreferencesData.getBoolean("editor.external")); + + checkUpdatesBox.setSelected(PreferencesData.getBoolean("update.check")); + + updateExtensionBox.setSelected(PreferencesData.get("editor.update_extension") == null || PreferencesData.getBoolean("editor.update_extension")); + + if (autoAssociateBox != null) { + autoAssociateBox.setSelected(PreferencesData.getBoolean("platform.auto_file_type_associations")); + } + + saveVerifyUploadBox.setSelected(PreferencesData.getBoolean("editor.save_on_verify")); + + proxyHTTPServer.setText(PreferencesData.get("proxy.http.server")); + try { + proxyHTTPPort.setText(Integer.toString(PreferencesData.getInteger("proxy.http.port", 8080))); + } catch (NumberFormatException e) { + proxyHTTPPort.setText(""); + } + proxyHTTPSServer.setText(PreferencesData.get("proxy.https.server")); + try { + proxyHTTPSPort.setText(Integer.toString(PreferencesData.getInteger("proxy.https.port", 8443))); + } catch (NumberFormatException e) { + proxyHTTPSPort.setText(""); + } + proxyUser.setText(PreferencesData.get("proxy.user")); + proxyPassword.setText(PreferencesData.get("proxy.password")); + + additionalBoardsManagerField.setText(PreferencesData.get("boardsmanager.additional.urls")); + } +} diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index ea5c67466b9..41e53169dea 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -92,9 +92,6 @@ public boolean apply(UserLibrary library) { public static SplashScreenHelper splashScreenHelper = new SplashScreenHelper(SplashScreen.getSplashScreen()); - // A single instance of the preferences window - Preferences preferencesFrame; - // set to true after the first time the menu is built. // so that the errors while building don't show up again. boolean builtOnce; @@ -1123,7 +1120,7 @@ protected boolean handleQuitEach() { * Asynchronous version of menu rebuild to be used on save and rename * to prevent the interface from locking up until the menus are done. */ - protected void rebuildSketchbookMenus() { + public void rebuildSketchbookMenus() { //System.out.println("async enter"); //new Exception().printStackTrace(); SwingUtilities.invokeLater(new Runnable() { @@ -1815,8 +1812,13 @@ public void mousePressed(MouseEvent e) { * Show the Preferences window. */ public void handlePrefs() { - if (preferencesFrame == null) preferencesFrame = new Preferences(); - preferencesFrame.showFrame(activeEditor); + cc.arduino.view.preferences.Preferences dialog = new cc.arduino.view.preferences.Preferences(activeEditor, this); + if (activeEditor != null) { + dialog.setLocationRelativeTo(activeEditor); + } + dialog.pack(); + dialog.setMinimumSize(dialog.getSize()); + dialog.setVisible(true); } @@ -2025,7 +2027,7 @@ static public File getSketchbookHardwareFolder() { } - protected File getDefaultSketchbookFolderOrPromptForIt() { + public File getDefaultSketchbookFolderOrPromptForIt() { File sketchbookFolder = BaseNoGui.getDefaultSketchbookFolder(); @@ -2119,7 +2121,7 @@ static public void openFolder(File file) { // ................................................................. - static public File selectFolder(String prompt, File folder, Frame frame) { + static public File selectFolder(String prompt, File folder, Component parent) { JFileChooser fc = new JFileChooser(); fc.setDialogTitle(prompt); if (folder != null) { @@ -2127,7 +2129,7 @@ static public File selectFolder(String prompt, File folder, Frame frame) { } fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - int returned = fc.showOpenDialog(new JDialog()); + int returned = fc.showOpenDialog(parent); if (returned == JFileChooser.APPROVE_OPTION) { return fc.getSelectedFile(); } diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index ef87a62191e..37d258ed102 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -258,7 +258,7 @@ public void windowDeactivated(WindowEvent e) { scrollPane = new RTextScrollPane(textarea, true); scrollPane.setBorder(new MatteBorder(0, 6, 0, 0, Theme.getColor("editor.bgcolor"))); scrollPane.setViewportBorder(BorderFactory.createEmptyBorder()); - scrollPane.setLineNumbersEnabled(Preferences.getBoolean("editor.linenumbers")); + scrollPane.setLineNumbersEnabled(PreferencesData.getBoolean("editor.linenumbers")); scrollPane.setIconRowHeaderEnabled(false); Gutter gutter = scrollPane.getGutter(); @@ -443,7 +443,7 @@ protected int[] getPlacement() { * the app is just starting up, or the user just finished messing * with things in the Preferences window. */ - protected void applyPreferences() { + public void applyPreferences() { // apply the setting for 'use external editor' boolean external = PreferencesData.getBoolean("editor.external"); @@ -1794,7 +1794,7 @@ protected void setCode(final SketchCodeDocument codeDoc) { if (document == null) { // this document not yet inited document = new RSyntaxDocument(new ArduinoTokenMakerFactory(base.getPdeKeywords()), RSyntaxDocument.SYNTAX_STYLE_CPLUSPLUS); - document.putProperty(PlainDocument.tabSizeAttribute, Preferences.getInteger("editor.tabs.size")); + document.putProperty(PlainDocument.tabSizeAttribute, PreferencesData.getInteger("editor.tabs.size")); // insert the program text into the document object try { diff --git a/app/src/processing/app/Preferences.java b/app/src/processing/app/Preferences.java index f55b976af1f..6f76d6e4f29 100644 --- a/app/src/processing/app/Preferences.java +++ b/app/src/processing/app/Preferences.java @@ -21,44 +21,30 @@ package processing.app; -import cc.arduino.view.preferences.AdditionalBoardsManagerURLTextArea; -import com.google.common.base.Function; -import com.google.common.base.Functions; -import com.google.common.base.Joiner; -import com.google.common.collect.Collections2; -import processing.app.helpers.FileUtils; -import processing.app.helpers.OSUtils; import processing.app.helpers.PreferencesHelper; import processing.app.helpers.PreferencesMap; -import processing.app.legacy.PApplet; -import javax.swing.*; import java.awt.*; -import java.awt.event.*; import java.io.File; -import java.util.*; -import java.util.List; - -import static processing.app.I18n._; /** * Storage class for user preferences and environment settings. - *

+ *

* This class no longer uses the Properties class, since * properties files are iso8859-1, which is highly likely to * be a problem when trying to save sketch folders and locations. - *

+ *

* The GUI portion in here is really ugly, as it uses exact layout. This was * done in frustration one evening (and pre-Swing), but that's long since past, * and it should all be moved to a proper swing layout like BoxLayout. - *

+ *

* This is very poorly put together, that the preferences panel and the actual * preferences i/o is part of the same code. But there hasn't yet been a * compelling reason to bother with the separation aside from concern about * being lectured by strangers who feel that it doesn't look like what they * learned in CS class. - *

+ *

* Would also be possible to change this to use the Java Preferences API. * Some useful articles * here and @@ -71,131 +57,12 @@ */ public class Preferences { - class Language { - Language(String _name, String _originalName, String _isoCode) { - name = _name; - originalName = _originalName; - isoCode = _isoCode; - } - - public String toString() { - if (originalName.length() == 0) - return name; - return originalName + " (" + name + ")"; - }; - - String name; - String originalName; - String isoCode; - } - - Language languages[] = { - new Language(_("System Default"), "", ""), - new Language(_("Albanian"), "shqip", "sq"), - new Language(_("Arabic"), "العربية", "ar"), - new Language(_("Aragonese"), "Aragonés", "an"), - new Language(_("Belarusian"), "Беларуская мова", "be"), - new Language(_("Bulgarian"), "български", "bg"), - new Language(_("Catalan"), "Català", "ca"), - new Language(_("Chinese Simplified"), "简体中文", "zh_CN"), - new Language(_("Chinese Traditional"), "繁體中文", "zh_TW"), - new Language(_("Croatian"), "Hrvatski", "hr_HR"), - new Language(_("Czech (Czech Republic)"), "český (Czech Republic)", "cs_CZ"), - new Language(_("Danish (Denmark)"), "Dansk (Denmark)", "da_DK"), - new Language(_("Dutch"), "Nederlands", "nl"), - new Language(_("English"), "English", "en"), - new Language(_("English (United Kingdom)"), "English (United Kingdom)", "en_GB"), - new Language(_("Estonian"), "Eesti", "et"), - new Language(_("Estonian (Estonia)"), "Eesti keel", "et_EE"), - new Language(_("Filipino"), "Pilipino", "fil"), - new Language(_("Finnish"), "Suomi", "fi"), - new Language(_("French"), "Français", "fr"), - new Language(_("Canadian French"), "Canadienne-français", "fr_CA"), - new Language(_("Galician"), "Galego", "gl"), - new Language(_("Georgian"), "საქართველოს", "ka_GE"), - new Language(_("German"), "Deutsch", "de_DE"), - new Language(_("Greek"), "ελληνικά", "el_GR"), - new Language(_("Hebrew"), "עברית", "he"), - new Language(_("Hindi"), "हिंदी", "hi"), - new Language(_("Hungarian"), "Magyar", "hu"), - new Language(_("Indonesian"), "Bahasa Indonesia", "id"), - new Language(_("Italian"), "Italiano", "it_IT"), - new Language(_("Japanese"), "日本語", "ja_JP"), - new Language(_("Korean"), "한국어", "ko_KR"), - new Language(_("Latvian"), "Latviešu", "lv_LV"), - new Language(_("Lithuaninan"), "Lietuvių Kalba", "lt_LT"), - new Language(_("Norwegian Bokmål"), "Norsk bokmål", "nb_NO"), - new Language(_("Persian"), "فارسی", "fa"), - new Language(_("Polish"), "Język Polski", "pl"), - new Language(_("Portuguese (Brazil)"), "Português (Brazil)", "pt_BR"), - new Language(_("Portuguese (Portugal)"), "Português (Portugal)", "pt_PT"), - new Language(_("Romanian"), "Română", "ro"), - new Language(_("Russian"), "Русский", "ru"), - new Language(_("Slovenian"), "Slovenščina", "sl_SI"), - new Language(_("Spanish"), "Español", "es"), - new Language(_("Swedish"), "Svenska", "sv"), - new Language(_("Tamil"), "தமிழ்", "ta"), - new Language(_("Turkish"), "Türk", "tr"), - new Language(_("Ukrainian"), "Український", "uk"), - new Language(_("Vietnamese"), "Tiếng Việt", "vi"), - }; - - // Incomplete languages - Language missingLanguages[] = { - new Language(_("Afrikaans"), "Afrikaans", "af"), - new Language(_("Armenian"), "Հայերեն", "hy"), - new Language(_("Asturian"), "Asturianu", "ast"), - new Language(_("Basque"), "Euskara", "eu"), - new Language(_("Bengali (India)"), "বাংলা (India)", "bn_IN"), - new Language(_("Bosnian"), "Bosanski", "bs"), - new Language(_("Burmese (Myanmar)"), "ဗမာစကား", "my_MM"), - new Language(_("Chinese (China)"), "", "zh_CN"), - new Language(_("Chinese (Hong Kong)"), "", "zh_HK"), - new Language(_("Chinese (Taiwan)"), "", "zh_TW"), - new Language(_("Chinese (Taiwan) (Big5)"), "", "zh_TW.Big5"), - new Language(_("Czech"), "český", "cs"), - new Language(_("Danish"), "Dansk", "da"), - new Language(_("Dutch (Netherlands)"), "Nederlands", "nl_NL"), - new Language(_("Galician (Spain)"), "Galego (Spain)", "gl_ES"), - new Language(_("Nepali"), "नेपाली", "ne"), - new Language(_("N'Ko"), "ߒߞߏ", "nqo"), - new Language(_("Marathi"), "मराठी", "mr"), - new Language(_("Malay (Malaysia)"), "بهاس ملايو (Malaysia)", "ms_MY"), - new Language(_("Norwegian"), "Norsk", "no"), - new Language(_("Norwegian Nynorsk"), "Norsk Nynorsk", "nn"), - new Language(_("Portugese"), "Português", "pt"), - new Language(_("Persian (Iran)"), "فارسی (Iran)", "fa_IR"), - new Language(_("Slovak"), "Slovenčina", "sk"), - new Language(_("Swahili"), "كِسوَهِل", "sw"), - new Language(_("Talossan"), "Talossan", "tzl"), - new Language(_("Urdu (Pakistan)"), "اردو (Pakistan)", "ur_PK"), - new Language(_("Western Frisian"), "Western Frisian", "fy"), - }; - - private static class WarningItem { - private final String value; - private final String translation; - - public WarningItem(String value, String translation) { - this.value = value; - this.translation = translation; - } - - public String getValue() { - return value; - } - - @Override - public String toString() { - return translation; - } - } /** * Standardized width for buttons. Mac OS X 10.3 wants 70 as its default, * Windows XP needs 66, and my Ubuntu machine needs 80+, so 80 seems proper. */ - static public int BUTTON_WIDTH = 80; + static public int BUTTON_WIDTH = 80; /** * Standardized button height. Mac OS X 10.3 (Java 1.4) wants 29, @@ -208,687 +75,22 @@ public String toString() { // value for the size bars, buttons, etc - static final int GRID_SIZE = 33; + static final int GRID_SIZE = 33; // indents and spacing standards. these probably need to be modified // per platform as well, since macosx is so huge, windows is smaller, // and linux is all over the map - static final int GUI_BIG = 13; - static final int GUI_BETWEEN = 5; - static final int GUI_SMALL = 6; - - // gui elements - - JFrame dialog; - int wide, high; - - JTextField sketchbookLocationField; - JCheckBox exportSeparateBox; - JCheckBox verboseCompilationBox; - JCheckBox verboseUploadBox; - JCheckBox displayLineNumbersBox; - JCheckBox enableCodeFoldingBox; - JCheckBox verifyUploadBox; - JCheckBox externalEditorBox; - JCheckBox checkUpdatesBox; - JTextField fontSizeField; - JCheckBox updateExtensionBox; - JCheckBox autoAssociateBox; - JComboBox comboLanguage; - JComboBox comboWarnings; - JCheckBox saveVerifyUploadBox; - JTextField proxyHTTPServer; - JTextField proxyHTTPPort; - JTextField proxyHTTPSServer; - JTextField proxyHTTPSPort; - JTextField proxyUser; - JPasswordField proxyPassword; - private final JTextField additionalBoardsManagerField; - - // the calling editor, so updates can be applied - - Editor editor; - + static final int GUI_SMALL = 6; static protected void init(File file) { - - PreferencesData.init(file); + PreferencesData.init(file); // other things that have to be set explicitly for the defaults PreferencesHelper.putColor(PreferencesData.prefs, "run.window.bgcolor", SystemColor.control); } - - public Preferences() { - - // setup dialog for the prefs - - //dialog = new JDialog(editor, "Preferences", true); - dialog = new JFrame(_("Preferences")); - dialog.setResizable(false); - - Container pane = dialog.getContentPane(); - pane.setLayout(null); - - int top = GUI_BIG; - int left = GUI_BIG; - int right = 0; - - JLabel label; - JButton button; //, button2; - //JComboBox combo; - Dimension d, d2; //, d3; - int h, vmax; - - - // Sketchbook location: - // [...............................] [ Browse ] - - label = new JLabel(_("Sketchbook location:")); - pane.add(label); - d = label.getPreferredSize(); - label.setBounds(left, top, d.width, d.height); - top += d.height; // + GUI_SMALL; - - sketchbookLocationField = new JTextField(40); - pane.add(sketchbookLocationField); - d = sketchbookLocationField.getPreferredSize(); - - button = new JButton(I18n.PROMPT_BROWSE); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - File dflt = new File(sketchbookLocationField.getText()); - File file = - Base.selectFolder(_("Select new sketchbook location"), dflt, dialog); - if (file != null) { - String path = file.getAbsolutePath(); - if (Base.getPortableFolder() != null) { - path = FileUtils.relativePath(Base.getPortableFolder().toString(), path); - if (path == null) { - path = Base.getPortableSketchbookFolder(); - } - } - sketchbookLocationField.setText(path); - } - } - }); - pane.add(button); - d2 = button.getPreferredSize(); - - // take max height of all components to vertically align em - vmax = Math.max(d.height, d2.height); - sketchbookLocationField.setBounds(left, top + (vmax-d.height)/2, - d.width, d.height); - h = left + d.width + GUI_SMALL; - button.setBounds(h, top + (vmax-d2.height)/2, - d2.width, d2.height); - - right = Math.max(right, h + d2.width + GUI_BIG); - top += vmax + GUI_BETWEEN; - - - // Preferred language: [ ] (requires restart of Arduino) - Container box = Box.createHorizontalBox(); - label = new JLabel(_("Editor language: ")); - box.add(label); - comboLanguage = new JComboBox(languages); - String currentLanguage = PreferencesData.get("editor.languages.current"); - for (Language language : languages) { - if (language.isoCode.equals(currentLanguage)) - comboLanguage.setSelectedItem(language); - } - box.add(comboLanguage); - label = new JLabel(_(" (requires restart of Arduino)")); - box.add(label); - pane.add(box); - d = box.getPreferredSize(); - box.setForeground(Color.gray); - box.setBounds(left, top, d.width, d.height); - right = Math.max(right, left + d.width); - top += d.height + GUI_BETWEEN; - - // Editor font size [ ] - - box = Box.createHorizontalBox(); - label = new JLabel(_("Editor font size: ")); - box.add(label); - fontSizeField = new JTextField(4); - box.add(fontSizeField); - label = new JLabel(_(" (requires restart of Arduino)")); - box.add(label); - pane.add(box); - d = box.getPreferredSize(); - box.setBounds(left, top, d.width, d.height); - Font editorFont = PreferencesData.getFont("editor.font"); - fontSizeField.setText(String.valueOf(editorFont.getSize())); - top += d.height + GUI_BETWEEN; - - // Show verbose output during: [ ] compilation [ ] upload - - box = Box.createHorizontalBox(); - label = new JLabel(_("Show verbose output during: ")); - box.add(label); - verboseCompilationBox = new JCheckBox(_("compilation ")); - box.add(verboseCompilationBox); - verboseUploadBox = new JCheckBox(_("upload")); - box.add(verboseUploadBox); - pane.add(box); - d = box.getPreferredSize(); - box.setBounds(left, top, d.width, d.height); - top += d.height + GUI_BETWEEN; - - // [ ] Enable all compiler warnings - - box = Box.createHorizontalBox(); - label = new JLabel(_("Compiler warnings: ")); - box.add(label); - WarningItem[] warningItems = new WarningItem[]{new WarningItem("none", _("None")), new WarningItem("default", _("Default")), new WarningItem("more", _("More")), new WarningItem("all", _("All")), }; - comboWarnings = new JComboBox(warningItems); - String currentWarningLevel = PreferencesData.get("compiler.warning_level", "none"); - for (WarningItem item : warningItems) { - if (currentWarningLevel.equals(item.getValue())) { - comboWarnings.setSelectedItem(item); - } - } - box.add(comboWarnings); - pane.add(box); - d = box.getPreferredSize(); - box.setForeground(Color.gray); - box.setBounds(left, top, d.width, d.height); - right = Math.max(right, left + d.width); - top += d.height + GUI_BETWEEN; - - // [ ] Display line numbers - - displayLineNumbersBox = new JCheckBox(_("Display line numbers")); - pane.add(displayLineNumbersBox); - d = displayLineNumbersBox.getPreferredSize(); - displayLineNumbersBox.setBounds(left, top, d.width + 10, d.height); - right = Math.max(right, left + d.width); - top += d.height + GUI_BETWEEN; - - // [ ] Enable Code Folding - - enableCodeFoldingBox = new JCheckBox(_("Enable Code Folding")); - pane.add(enableCodeFoldingBox); - d = enableCodeFoldingBox.getPreferredSize(); - enableCodeFoldingBox.setBounds(left, top, d.width + 10, d.height); - right = Math.max(right, left + d.width); - top += d.height + GUI_BETWEEN; - - // [ ] Verify code after upload - - verifyUploadBox = new JCheckBox(_("Verify code after upload")); - pane.add(verifyUploadBox); - d = verifyUploadBox.getPreferredSize(); - verifyUploadBox.setBounds(left, top, d.width + 10, d.height); - right = Math.max(right, left + d.width); - top += d.height + GUI_BETWEEN; - - // [ ] Use external editor - - externalEditorBox = new JCheckBox(_("Use external editor")); - pane.add(externalEditorBox); - d = externalEditorBox.getPreferredSize(); - externalEditorBox.setBounds(left, top, d.width + 10, d.height); - right = Math.max(right, left + d.width); - top += d.height + GUI_BETWEEN; - - - // [ ] Check for updates on startup - - checkUpdatesBox = new JCheckBox(_("Check for updates on startup")); - pane.add(checkUpdatesBox); - d = checkUpdatesBox.getPreferredSize(); - checkUpdatesBox.setBounds(left, top, d.width + 10, d.height); - right = Math.max(right, left + d.width); - top += d.height + GUI_BETWEEN; - - // [ ] Update sketch files to new extension on save (.pde -> .ino) - - updateExtensionBox = new JCheckBox(_("Update sketch files to new extension on save (.pde -> .ino)")); - pane.add(updateExtensionBox); - d = updateExtensionBox.getPreferredSize(); - updateExtensionBox.setBounds(left, top, d.width + 10, d.height); - right = Math.max(right, left + d.width); - top += d.height + GUI_BETWEEN; - - // [ ] Automatically associate .pde files with Processing - - if (OSUtils.isWindows()) { - autoAssociateBox = - new JCheckBox(_("Automatically associate .ino files with Arduino")); - pane.add(autoAssociateBox); - d = autoAssociateBox.getPreferredSize(); - autoAssociateBox.setBounds(left, top, d.width + 10, d.height); - right = Math.max(right, left + d.width); - top += d.height + GUI_BETWEEN; - - // If using portable mode, it's bad manner to change PC setting. - if (Base.getPortableFolder() != null) - autoAssociateBox.setEnabled(false); - } - - // [ ] save when verifying or uploading - - saveVerifyUploadBox = new JCheckBox(_("Save when verifying or uploading")); - pane.add(saveVerifyUploadBox); - d = saveVerifyUploadBox.getPreferredSize(); - saveVerifyUploadBox.setBounds(left, top, d.width + 10, d.height); - right = Math.max(right, left + d.width); - top += d.height + GUI_BETWEEN; - - // proxy settings - - JPanel proxySettingsContainer = new JPanel(); - pane.add(proxySettingsContainer); - setupProxySettingsFieldSet(proxySettingsContainer); - d = proxySettingsContainer.getMinimumSize(); - proxySettingsContainer.setBounds(left, top, d.width + 10, d.height); - right = Math.max(right, left + d.width); - top += d.height + GUI_BETWEEN; - - // boards manager additional urls - box = Box.createHorizontalBox(); - label = new JLabel(_("Additional Boards Manager URLs: ")); - box.add(label); - additionalBoardsManagerField = new JTextField(30); - box.add(additionalBoardsManagerField); - JButton extendedAdditionalUrlFieldWindow = new JButton(new ImageIcon(Base.getThemeImage("newwindow.gif", dialog))); - extendedAdditionalUrlFieldWindow.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - final AdditionalBoardsManagerURLTextArea additionalBoardsManagerURLTextArea = new AdditionalBoardsManagerURLTextArea(dialog); - additionalBoardsManagerURLTextArea.setText(additionalBoardsManagerField.getText()); - additionalBoardsManagerURLTextArea.onOk(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - additionalBoardsManagerField.setText(additionalBoardsManagerURLTextArea.getText()); - } - }); - additionalBoardsManagerURLTextArea.setVisible(true); - } - }); - box.add(extendedAdditionalUrlFieldWindow); - pane.add(box); - d = box.getPreferredSize(); - box.setBounds(left, top, d.width, d.height); - top += d.height + GUI_BETWEEN; - - // More preferences are in the ... - - label = new JLabel(_("More preferences can be edited directly in the file")); - pane.add(label); - d = label.getPreferredSize(); - label.setForeground(Color.gray); - label.setBounds(left, top, d.width, d.height); - right = Math.max(right, left + d.width); - top += d.height; // + GUI_SMALL; - - label = new JLabel(PreferencesData.preferencesFile.getAbsolutePath()); - final JLabel clickable = label; - label.addMouseListener(new MouseAdapter() { - public void mousePressed(MouseEvent e) { - Base.openFolder(PreferencesData.preferencesFile.getParentFile()); - } - - public void mouseEntered(MouseEvent e) { - clickable.setForeground(new Color(0, 0, 140)); - } - - public void mouseExited(MouseEvent e) { - clickable.setForeground(Color.BLACK); - } - }); - pane.add(label); - d = label.getPreferredSize(); - label.setBounds(left, top, d.width, d.height); - right = Math.max(right, left + d.width); - top += d.height; - - label = new JLabel(_("(edit only when Arduino is not running)")); - pane.add(label); - d = label.getPreferredSize(); - label.setForeground(Color.gray); - label.setBounds(left, top, d.width, d.height); - right = Math.max(right, left + d.width); - top += d.height; // + GUI_SMALL; - - - // [ OK ] [ Cancel ] maybe these should be next to the message? - - button = new JButton(I18n.PROMPT_OK); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - applyFrame(); - disposeFrame(); - } - }); - pane.add(button); - d2 = button.getPreferredSize(); - BUTTON_HEIGHT = d2.height; - - h = right - (BUTTON_WIDTH + GUI_SMALL + BUTTON_WIDTH); - button.setBounds(h, top, BUTTON_WIDTH, BUTTON_HEIGHT); - h += BUTTON_WIDTH + GUI_SMALL; - - button = new JButton(I18n.PROMPT_CANCEL); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - disposeFrame(); - } - }); - pane.add(button); - button.setBounds(h, top, BUTTON_WIDTH, BUTTON_HEIGHT); - - top += BUTTON_HEIGHT + GUI_BETWEEN; - - - // finish up - - wide = right + GUI_BIG; - high = top + GUI_SMALL; - - - // closing the window is same as hitting cancel button - - dialog.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - disposeFrame(); - } - }); - - ActionListener disposer = new ActionListener() { - public void actionPerformed(ActionEvent actionEvent) { - disposeFrame(); - } - }; - Base.registerWindowCloseKeys(dialog.getRootPane(), disposer); - Base.setIcon(dialog); - - Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); - dialog.setLocation((screen.width - wide) / 2, - (screen.height - high) / 2); - - dialog.pack(); // get insets - Insets insets = dialog.getInsets(); - dialog.setSize(wide + insets.left + insets.right, - high + insets.top + insets.bottom); - - - // handle window closing commands for ctrl/cmd-W or hitting ESC. - - pane.addKeyListener(new KeyAdapter() { - public void keyPressed(KeyEvent e) { - //System.out.println(e); - KeyStroke wc = Editor.WINDOW_CLOSE_KEYSTROKE; - if ((e.getKeyCode() == KeyEvent.VK_ESCAPE) || - (KeyStroke.getKeyStrokeForEvent(e).equals(wc))) { - disposeFrame(); - } - } - }); - } - - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void setupProxySettingsFieldSet(Container pane) { - - JPanel proxySettingsPanel = new JPanel(); - - JLabel proxyHTTPServerLabel = new JLabel(); - proxyHTTPServer = new JTextField(); - JLabel proxyHTTPPortLabel = new JLabel(); - proxyHTTPPort = new JTextField(); - JLabel proxyHTTPSServerLabel = new JLabel(); - proxyHTTPSServer = new JTextField(); - JLabel proxyHTTPSPortLabel = new JLabel(); - proxyHTTPSPort = new JTextField(); - JLabel proxyUserLabel = new JLabel(); - proxyUser = new JTextField(); - JLabel proxyPasswordLabel = new JLabel(); - proxyPassword = new JPasswordField(); - - proxySettingsPanel.setBorder(BorderFactory.createTitledBorder(_("Proxy Settings"))); - - proxyHTTPServerLabel.setText(_("Server (HTTP):")); - - proxyHTTPServer.setColumns(10); - - proxyHTTPPortLabel.setText(_("Port (HTTP):")); - - proxyHTTPPort.setColumns(10); - - proxyHTTPSServerLabel.setText(_("Server: (HTTPS)")); - - proxyHTTPSServer.setColumns(10); - - proxyHTTPSPortLabel.setText(_("Port (HTTPS):")); - - proxyHTTPSPort.setColumns(10); - - proxyUserLabel.setText(_("Username:")); - - proxyPasswordLabel.setText(_("Password:")); - - GroupLayout proxySettingsPanelLayout = new GroupLayout(proxySettingsPanel); - proxySettingsPanel.setLayout(proxySettingsPanelLayout); - proxySettingsPanelLayout.setHorizontalGroup( - proxySettingsPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(proxySettingsPanelLayout.createSequentialGroup() - .addContainerGap() - .addGroup(proxySettingsPanelLayout.createParallelGroup(GroupLayout.Alignment.TRAILING) - .addComponent(proxyUserLabel) - .addComponent(proxyHTTPSServerLabel) - .addComponent(proxyHTTPServerLabel)) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addGroup(proxySettingsPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING, false) - .addComponent(proxyHTTPServer) - .addComponent(proxyHTTPSServer) - .addComponent(proxyUser)) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addGroup(proxySettingsPanelLayout.createParallelGroup(GroupLayout.Alignment.TRAILING) - .addComponent(proxyPasswordLabel) - .addComponent(proxyHTTPSPortLabel) - .addComponent(proxyHTTPPortLabel)) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addGroup(proxySettingsPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING, false) - .addComponent(proxyPassword) - .addComponent(proxyHTTPSPort) - .addComponent(proxyHTTPPort)) - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - proxySettingsPanelLayout.setVerticalGroup( - proxySettingsPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(proxySettingsPanelLayout.createSequentialGroup() - .addGroup(proxySettingsPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(proxyHTTPServerLabel) - .addComponent(proxyHTTPServer, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addComponent(proxyHTTPPortLabel) - .addComponent(proxyHTTPPort, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addGroup(proxySettingsPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(proxyHTTPSServerLabel) - .addComponent(proxyHTTPSServer, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addComponent(proxyHTTPSPortLabel) - .addComponent(proxyHTTPSPort, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addGroup(proxySettingsPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(proxyUserLabel) - .addComponent(proxyUser, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addComponent(proxyPasswordLabel) - .addComponent(proxyPassword, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))) - ); - - GroupLayout layout = new GroupLayout(pane); - pane.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(proxySettingsPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(proxySettingsPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) - ); - - }// //GEN-END:initComponents - // ................................................................. - - - /** - * Close the window after an OK or Cancel. - */ - protected void disposeFrame() { - dialog.dispose(); - } - - - /** - * Change internal settings based on what was chosen in the prefs, - * then send a message to the editor saying that it's time to do the same. - */ - protected void applyFrame() { - // put each of the settings into the table - PreferencesData.setBoolean("build.verbose", verboseCompilationBox.isSelected()); - PreferencesData.setBoolean("upload.verbose", verboseUploadBox.isSelected()); - PreferencesData.setBoolean("editor.linenumbers", displayLineNumbersBox.isSelected()); - PreferencesData.setBoolean("editor.code_folding", enableCodeFoldingBox.isSelected()); - PreferencesData.setBoolean("upload.verify", verifyUploadBox.isSelected()); - PreferencesData.setBoolean("editor.save_on_verify", saveVerifyUploadBox.isSelected()); - -// setBoolean("sketchbook.closing_last_window_quits", -// closingLastQuitsBox.isSelected()); - //setBoolean("sketchbook.prompt", sketchPromptBox.isSelected()); - //setBoolean("sketchbook.auto_clean", sketchCleanBox.isSelected()); - - // if the sketchbook path has changed, rebuild the menus - String oldPath = PreferencesData.get("sketchbook.path"); - String newPath = sketchbookLocationField.getText(); - if (newPath.isEmpty()) { - if (Base.getPortableFolder() == null) - newPath = editor.base.getDefaultSketchbookFolderOrPromptForIt().toString(); - else - newPath = Base.getPortableSketchbookFolder(); - } - if (!newPath.equals(oldPath)) { - editor.base.rebuildSketchbookMenus(); - PreferencesData.set("sketchbook.path", newPath); - } - - PreferencesData.setBoolean("editor.external", externalEditorBox.isSelected()); - PreferencesData.setBoolean("update.check", checkUpdatesBox.isSelected()); - PreferencesData.setBoolean("editor.save_on_verify", saveVerifyUploadBox.isSelected()); - - /* - // was gonna use this to check memory settings, - // but it quickly gets much too messy - if (getBoolean("run.options.memory")) { - Process process = Runtime.getRuntime().exec(new String[] { - "java", "-Xms" + memoryMin + "m", "-Xmx" + memoryMax + "m" - }); - processInput = new SystemOutSiphon(process.getInputStream()); - processError = new MessageSiphon(process.getErrorStream(), this); - } - */ - - String newSizeText = fontSizeField.getText(); - try { - int newSize = Integer.parseInt(newSizeText.trim()); - String pieces[] = PApplet.split(PreferencesData.get("editor.font"), ','); - pieces[2] = String.valueOf(newSize); - PreferencesData.set("editor.font", PApplet.join(pieces, ',')); - - } catch (Exception e) { - System.err.println(I18n.format(_("ignoring invalid font size {0}"), newSizeText)); - } - - if (autoAssociateBox != null) { - PreferencesData.setBoolean("platform.auto_file_type_associations", autoAssociateBox.isSelected()); - } - - PreferencesData.setBoolean("editor.update_extension", updateExtensionBox.isSelected()); - - // adds the selected language to the preferences file - Language newLanguage = (Language) comboLanguage.getSelectedItem(); - PreferencesData.set("editor.languages.current", newLanguage.isoCode); - - WarningItem warningItem = (WarningItem) comboWarnings.getSelectedItem(); - PreferencesData.set("compiler.warning_level", warningItem.getValue()); - - PreferencesData.set("proxy.http.server", proxyHTTPServer.getText()); - try { - PreferencesData.set("proxy.http.port", Integer.valueOf(proxyHTTPPort.getText()).toString()); - } catch (NumberFormatException e) { - PreferencesData.remove("proxy.http.port"); - } - PreferencesData.set("proxy.https.server", proxyHTTPSServer.getText()); - try { - PreferencesData.set("proxy.https.port", Integer.valueOf(proxyHTTPSPort.getText()).toString()); - } catch (NumberFormatException e) { - PreferencesData.remove("proxy.https.port"); - } - PreferencesData.set("proxy.user", proxyUser.getText()); - PreferencesData.set("proxy.password", new String(proxyPassword.getPassword())); - - PreferencesData.set("boardsmanager.additional.urls", additionalBoardsManagerField.getText().replace("\r\n", "\n").replace("\r", "\n").replace("\n", ",")); - - editor.applyPreferences(); - } - - - protected void showFrame(Editor editor) { - this.editor = editor; - - // set all settings entry boxes to their actual status - verboseCompilationBox.setSelected(PreferencesData.getBoolean("build.verbose")); - verboseUploadBox.setSelected(PreferencesData.getBoolean("upload.verbose")); - displayLineNumbersBox.setSelected(PreferencesData.getBoolean("editor.linenumbers")); - enableCodeFoldingBox.setSelected(PreferencesData.getBoolean("editor.code_folding")); - verifyUploadBox.setSelected(PreferencesData.getBoolean("upload.verify")); - - //closingLastQuitsBox. - // setSelected(getBoolean("sketchbook.closing_last_window_quits")); - //sketchPromptBox. - // setSelected(getBoolean("sketchbook.prompt")); - //sketchCleanBox. - // setSelected(getBoolean("sketchbook.auto_clean")); - - sketchbookLocationField.setText(PreferencesData.get("sketchbook.path")); - externalEditorBox.setSelected(PreferencesData.getBoolean("editor.external")); - checkUpdatesBox.setSelected(PreferencesData.getBoolean("update.check")); - saveVerifyUploadBox.setSelected(PreferencesData.getBoolean("editor.save_on_verify")); - - if (autoAssociateBox != null) { - autoAssociateBox.setSelected(PreferencesData.getBoolean("platform.auto_file_type_associations")); - } - - updateExtensionBox.setSelected(PreferencesData.get("editor.update_extension") == null || PreferencesData.getBoolean("editor.update_extension")); - - proxyHTTPServer.setText(PreferencesData.get("proxy.http.server")); - try { - proxyHTTPPort.setText(Integer.toString(PreferencesData.getInteger("proxy.http.port", 8080))); - } catch (NumberFormatException e) { - proxyHTTPPort.setText(""); - } - proxyHTTPSServer.setText(PreferencesData.get("proxy.https.server")); - try { - proxyHTTPSPort.setText(Integer.toString(PreferencesData.getInteger("proxy.https.port", 8443))); - } catch (NumberFormatException e) { - proxyHTTPSPort.setText(""); - } - proxyUser.setText(PreferencesData.get("proxy.user")); - proxyPassword.setText(PreferencesData.get("proxy.password")); - - additionalBoardsManagerField.setText(PreferencesData.get("boardsmanager.additional.urls")); - - dialog.setLocationRelativeTo(editor); - dialog.setVisible(true); - } - @Deprecated protected static void save() { PreferencesData.save(); diff --git a/app/src/processing/app/syntax/SketchTextAreaDefaultInputMap.java b/app/src/processing/app/syntax/SketchTextAreaDefaultInputMap.java index bf134b60846..5d806f4c9f9 100644 --- a/app/src/processing/app/syntax/SketchTextAreaDefaultInputMap.java +++ b/app/src/processing/app/syntax/SketchTextAreaDefaultInputMap.java @@ -33,8 +33,11 @@ public SketchTextAreaDefaultInputMap() { if (isOSX) { put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, defaultModifier), SketchTextAreaEditorKit.rtaDeleteLineToCursorAction); + put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, defaultModifier), DefaultEditorKit.beginAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, defaultModifier), DefaultEditorKit.endAction); + + remove(KeyStroke.getKeyStroke(KeyEvent.VK_J, defaultModifier)); } } } diff --git a/arduino-core/src/processing/app/I18n.java b/arduino-core/src/processing/app/I18n.java index d6480d99ef7..ef71ce3652a 100644 --- a/arduino-core/src/processing/app/I18n.java +++ b/arduino-core/src/processing/app/I18n.java @@ -24,11 +24,11 @@ public class I18n { // prompt text stuff - static String PROMPT_YES; - static String PROMPT_NO; - static String PROMPT_CANCEL; - static String PROMPT_OK; - static String PROMPT_BROWSE; + public static String PROMPT_YES; + public static String PROMPT_NO; + public static String PROMPT_CANCEL; + public static String PROMPT_OK; + public static String PROMPT_BROWSE; static protected void init(String language) throws MissingResourceException { String[] languageParts = language.split("_"); diff --git a/arduino-core/src/processing/app/PreferencesData.java b/arduino-core/src/processing/app/PreferencesData.java index 6ee44fb344e..7733b08e4e5 100644 --- a/arduino-core/src/processing/app/PreferencesData.java +++ b/arduino-core/src/processing/app/PreferencesData.java @@ -88,6 +88,10 @@ static public void init(File file) { fixPreferences(); } + public static File getPreferencesFile() { + return preferencesFile; + } + private static void fixPreferences() { String baud = get("serial.debug_rate"); if ("14400".equals(baud) || "28800".equals(baud)) { diff --git a/libraries/Firmata/README.adoc b/libraries/Firmata/README.adoc index a85d485404c..274865ce4a6 100644 --- a/libraries/Firmata/README.adoc +++ b/libraries/Firmata/README.adoc @@ -1,14 +1,17 @@ = Firmata Library for Arduino = -The Firmata library implements the Firmata protocol for communicating with software on the host computer. This allows you to write custom firmware without having to create your own protocol and objects for the programming environment that you are using. +The Firmata library implements the Firmata protocol for communicating with software on the host computer. This allows you to write custom firmware without having to create your own protocol and objects for the programming environment that you are using. -For more information about this library please visit us at -http://arduino.cc/en/Reference/Firmata +The Firmata source code is hosted outside of the Arduino code repository. Please visit the Firmata +GitHub repository at https://github.com/firmata/arduino to file issues, make contributions or obtain the latest version of the library. + +To learn more about the Firmata protocol, see the protocol documentation site at https://github.com/firmata/protocol. + +Documentation is also available at http://arduino.cc/en/Reference/Firmata. == License == -Copyright (c) 2006-2008 Hans-Christoph Steiner. All rights reserved. -Copyright (c) 2010 Arduino LLC. All right reserved. +Copyright (c) 2006-2008 Hans-Christoph Steiner. All rights reserved. Copyright (c) 2010 Arduino LLC. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/libraries/Firmata/examples/AllInputsFirmata/AllInputsFirmata.ino b/libraries/Firmata/examples/AllInputsFirmata/AllInputsFirmata.ino index cfe44820a7b..b6f766d7c06 100644 --- a/libraries/Firmata/examples/AllInputsFirmata/AllInputsFirmata.ino +++ b/libraries/Firmata/examples/AllInputsFirmata/AllInputsFirmata.ino @@ -22,7 +22,7 @@ byte pin; int analogValue; int previousAnalogValues[TOTAL_ANALOG_PINS]; -byte portStatus[TOTAL_PORTS]; // each bit: 1=pin is digital input, 0=other/ignore +byte portStatus[TOTAL_PORTS]; // each bit: 1=pin is digital input, 0=other/ignore byte previousPINs[TOTAL_PORTS]; /* timer variables */ @@ -45,7 +45,7 @@ void setup() { byte i, port, status; - Firmata.setFirmwareVersion(0, 1); + Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION); for (pin = 0; pin < TOTAL_PINS; pin++) { if IS_PIN_DIGITAL(pin) pinMode(PIN_TO_DIGITAL(pin), INPUT); diff --git a/libraries/Firmata/examples/AnalogFirmata/AnalogFirmata.ino b/libraries/Firmata/examples/AnalogFirmata/AnalogFirmata.ino index 8c4d9cd4959..de86f5f45e4 100644 --- a/libraries/Firmata/examples/AnalogFirmata/AnalogFirmata.ino +++ b/libraries/Firmata/examples/AnalogFirmata/AnalogFirmata.ino @@ -53,7 +53,7 @@ void analogWriteCallback(byte pin, int value) void reportAnalogCallback(byte pin, int value) { if (value == 0) { - analogInputsToReport = analogInputsToReport &~ (1 << pin); + analogInputsToReport = analogInputsToReport & ~ (1 << pin); } else { // everything but 0 enables reporting of that pin analogInputsToReport = analogInputsToReport | (1 << pin); @@ -66,7 +66,7 @@ void reportAnalogCallback(byte pin, int value) *============================================================================*/ void setup() { - Firmata.setFirmwareVersion(0, 2); + Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION); Firmata.attach(ANALOG_MESSAGE, analogWriteCallback); Firmata.attach(REPORT_ANALOG, reportAnalogCallback); diff --git a/libraries/Firmata/examples/EchoString/EchoString.ino b/libraries/Firmata/examples/EchoString/EchoString.ino index eea90958757..84fb3ea20e9 100644 --- a/libraries/Firmata/examples/EchoString/EchoString.ino +++ b/libraries/Firmata/examples/EchoString/EchoString.ino @@ -21,14 +21,14 @@ void stringCallback(char *myString) } -void sysexCallback(byte command, byte argc, byte*argv) +void sysexCallback(byte command, byte argc, byte *argv) { Firmata.sendSysex(command, argc, argv); } void setup() { - Firmata.setFirmwareVersion(0, 1); + Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION); Firmata.attach(STRING_DATA, stringCallback); Firmata.attach(START_SYSEX, sysexCallback); Firmata.begin(57600); diff --git a/libraries/Firmata/examples/OldStandardFirmata/OldStandardFirmata.ino b/libraries/Firmata/examples/OldStandardFirmata/OldStandardFirmata.ino index 761f388803f..ea987c403dc 100644 --- a/libraries/Firmata/examples/OldStandardFirmata/OldStandardFirmata.ino +++ b/libraries/Firmata/examples/OldStandardFirmata/OldStandardFirmata.ino @@ -55,7 +55,7 @@ unsigned long previousMillis; // for comparison with currentMillis void outputPort(byte portNumber, byte portValue) { - portValue = portValue &~ portStatus[portNumber]; + portValue = portValue & ~ portStatus[portNumber]; if (previousPINs[portNumber] != portValue) { Firmata.sendDigitalPort(portNumber, portValue); previousPINs[portNumber] = portValue; @@ -72,7 +72,7 @@ void checkDigitalInputs(void) for (i = 0; i < TOTAL_PORTS; i++) { if (reportPINs[i]) { switch (i) { - case 0: outputPort(0, PIND &~ B00000011); break; // ignore Rx/Tx 0/1 + case 0: outputPort(0, PIND & ~ B00000011); break; // ignore Rx/Tx 0/1 case 1: outputPort(1, PINB); break; case 2: outputPort(2, PINC); break; } @@ -104,7 +104,7 @@ void setPinModeCallback(byte pin, int mode) { switch (mode) { case INPUT: pinMode(pin, INPUT); - portStatus[port] = portStatus[port] &~ (1 << (pin - offset)); + portStatus[port] = portStatus[port] & ~ (1 << (pin - offset)); break; case OUTPUT: digitalWrite(pin, LOW); // disable PWM @@ -112,7 +112,7 @@ void setPinModeCallback(byte pin, int mode) { pinMode(pin, OUTPUT); portStatus[port] = portStatus[port] | (1 << (pin - offset)); break; - //case ANALOG: // TODO figure this out + //case ANALOG: // TODO figure this out default: Firmata.sendString(""); } @@ -131,7 +131,7 @@ void digitalWriteCallback(byte port, int value) switch (port) { case 0: // pins 2-7 (don't change Rx/Tx, pins 0 and 1) // 0xFF03 == B1111111100000011 0x03 == B00000011 - PORTD = (value &~ 0xFF03) | (PORTD & 0x03); + PORTD = (value & ~ 0xFF03) | (PORTD & 0x03); break; case 1: // pins 8-13 (14,15 are disabled for the crystal) PORTB = (byte)value; @@ -150,7 +150,7 @@ void digitalWriteCallback(byte port, int value) void reportAnalogCallback(byte pin, int value) { if (value == 0) { - analogInputsToReport = analogInputsToReport &~ (1 << pin); + analogInputsToReport = analogInputsToReport & ~ (1 << pin); } else { // everything but 0 enables reporting of that pin analogInputsToReport = analogInputsToReport | (1 << pin); @@ -202,7 +202,7 @@ void setup() /* send digital inputs here, if enabled, to set the initial state on the * host computer, since once in the loop(), this firmware will only send * digital data on change. */ - if (reportPINs[0]) outputPort(0, PIND &~ B00000011); // ignore Rx/Tx 0/1 + if (reportPINs[0]) outputPort(0, PIND & ~ B00000011); // ignore Rx/Tx 0/1 if (reportPINs[1]) outputPort(1, PINB); if (reportPINs[2]) outputPort(2, PINC); diff --git a/libraries/Firmata/examples/ServoFirmata/ServoFirmata.ino b/libraries/Firmata/examples/ServoFirmata/ServoFirmata.ino index aab189bd792..2df192971fc 100644 --- a/libraries/Firmata/examples/ServoFirmata/ServoFirmata.ino +++ b/libraries/Firmata/examples/ServoFirmata/ServoFirmata.ino @@ -12,8 +12,6 @@ /* This firmware supports as many servos as possible using the Servo library * included in Arduino 0017 * - * TODO add message to configure minPulse/maxPulse/degrees - * * This example code is in the public domain. */ @@ -21,28 +19,43 @@ #include Servo servos[MAX_SERVOS]; +byte servoPinMap[TOTAL_PINS]; +byte servoCount = 0; void analogWriteCallback(byte pin, int value) { - if (IS_PIN_SERVO(pin)) { - servos[PIN_TO_SERVO(pin)].write(value); + if (IS_PIN_DIGITAL(pin)) { + servos[servoPinMap[pin]].write(value); } } +void systemResetCallback() +{ + servoCount = 0; +} + void setup() { byte pin; - Firmata.setFirmwareVersion(0, 2); + Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION); Firmata.attach(ANALOG_MESSAGE, analogWriteCallback); + Firmata.attach(SYSTEM_RESET, systemResetCallback); + Firmata.begin(57600); + systemResetCallback(); + + // attach servos from first digital pin up to max number of + // servos supported for the board for (pin = 0; pin < TOTAL_PINS; pin++) { - if (IS_PIN_SERVO(pin)) { - servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin)); + if (IS_PIN_DIGITAL(pin)) { + if (servoCount < MAX_SERVOS) { + servoPinMap[pin] = servoCount; + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin)); + servoCount++; + } } } - - Firmata.begin(57600); } void loop() @@ -50,4 +63,3 @@ void loop() while (Firmata.available()) Firmata.processInput(); } - diff --git a/libraries/Firmata/examples/SimpleAnalogFirmata/SimpleAnalogFirmata.ino b/libraries/Firmata/examples/SimpleAnalogFirmata/SimpleAnalogFirmata.ino index 63ef465c63f..de7c4e2c780 100644 --- a/libraries/Firmata/examples/SimpleAnalogFirmata/SimpleAnalogFirmata.ino +++ b/libraries/Firmata/examples/SimpleAnalogFirmata/SimpleAnalogFirmata.ino @@ -27,7 +27,7 @@ void analogWriteCallback(byte pin, int value) void setup() { - Firmata.setFirmwareVersion(0, 1); + Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION); Firmata.attach(ANALOG_MESSAGE, analogWriteCallback); Firmata.begin(57600); } diff --git a/libraries/Firmata/examples/SimpleDigitalFirmata/SimpleDigitalFirmata.ino b/libraries/Firmata/examples/SimpleDigitalFirmata/SimpleDigitalFirmata.ino index 016c22091a0..a2b74179022 100644 --- a/libraries/Firmata/examples/SimpleDigitalFirmata/SimpleDigitalFirmata.ino +++ b/libraries/Firmata/examples/SimpleDigitalFirmata/SimpleDigitalFirmata.ino @@ -52,7 +52,7 @@ void digitalWriteCallback(byte port, int value) void setup() { - Firmata.setFirmwareVersion(0, 1); + Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION); Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback); Firmata.attach(SET_PIN_MODE, setPinModeCallback); Firmata.begin(57600); diff --git a/libraries/Firmata/examples/StandardFirmata/StandardFirmata.ino b/libraries/Firmata/examples/StandardFirmata/StandardFirmata.ino index 330f39663c5..9dfb8c4fe62 100644 --- a/libraries/Firmata/examples/StandardFirmata/StandardFirmata.ino +++ b/libraries/Firmata/examples/StandardFirmata/StandardFirmata.ino @@ -1,19 +1,17 @@ /* - * Firmata is a generic protocol for communicating with microcontrollers - * from software on a host computer. It is intended to work with - * any host computer software package. - * - * To download a host software package, please clink on the following link - * to open the download page in your default browser. - * - * http://firmata.org/wiki/Download - */ + Firmata is a generic protocol for communicating with microcontrollers + from software on a host computer. It is intended to work with + any host computer software package. + + To download a host software package, please clink on the following link + to open the download page in your default browser. + + https://github.com/firmata/arduino#firmata-client-libraries -/* Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved. Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved. Copyright (C) 2009 Shigeru Kobayashi. All rights reserved. - Copyright (C) 2009-2011 Jeff Hoefs. All rights reserved. + Copyright (C) 2009-2015 Jeff Hoefs. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -22,29 +20,25 @@ See file LICENSE.txt for further informations on licensing terms. - formatted using the GNU C formatting and indenting + Last updated by Jeff Hoefs: April 11, 2015 */ -/* - * TODO: use Program Control to load stored profiles from EEPROM - */ - #include #include #include -// move the following defines to Firmata.h? -#define I2C_WRITE B00000000 -#define I2C_READ B00001000 -#define I2C_READ_CONTINUOUSLY B00010000 -#define I2C_STOP_READING B00011000 -#define I2C_READ_WRITE_MODE_MASK B00011000 +#define I2C_WRITE B00000000 +#define I2C_READ B00001000 +#define I2C_READ_CONTINUOUSLY B00010000 +#define I2C_STOP_READING B00011000 +#define I2C_READ_WRITE_MODE_MASK B00011000 #define I2C_10BIT_ADDRESS_MODE_MASK B00100000 +#define MAX_QUERIES 8 +#define REGISTER_NOT_SPECIFIED -1 -#define MAX_QUERIES 8 +// the minimum interval for sampling analog input #define MINIMUM_SAMPLING_INTERVAL 10 -#define REGISTER_NOT_SPECIFIED -1 /*============================================================================== * GLOBAL VARIABLES @@ -65,12 +59,12 @@ int pinState[TOTAL_PINS]; // any value that has been written /* timer variables */ unsigned long currentMillis; // store the current value from millis() unsigned long previousMillis; // for comparison with currentMillis -int samplingInterval = 19; // how often to run the main loop (in ms) +unsigned int samplingInterval = 19; // how often to run the main loop (in ms) /* i2c data */ struct i2c_device_info { byte addr; - byte reg; + int reg; byte bytes; }; @@ -80,24 +74,85 @@ i2c_device_info query[MAX_QUERIES]; byte i2cRxData[32]; boolean isI2CEnabled = false; signed char queryIndex = -1; -unsigned int i2cReadDelayTime = 0; // default delay time between i2c read request and Wire.requestFrom() +// default delay time between i2c read request and Wire.requestFrom() +unsigned int i2cReadDelayTime = 0; Servo servos[MAX_SERVOS]; +byte servoPinMap[TOTAL_PINS]; +byte detachedServos[MAX_SERVOS]; +byte detachedServoCount = 0; +byte servoCount = 0; + +boolean isResetting = false; + +/* utility functions */ +void wireWrite(byte data) +{ +#if ARDUINO >= 100 + Wire.write((byte)data); +#else + Wire.send(data); +#endif +} + +byte wireRead(void) +{ +#if ARDUINO >= 100 + return Wire.read(); +#else + return Wire.receive(); +#endif +} + /*============================================================================== * FUNCTIONS *============================================================================*/ +void attachServo(byte pin, int minPulse, int maxPulse) +{ + if (servoCount < MAX_SERVOS) { + // reuse indexes of detached servos until all have been reallocated + if (detachedServoCount > 0) { + servoPinMap[pin] = detachedServos[detachedServoCount - 1]; + if (detachedServoCount > 0) detachedServoCount--; + } else { + servoPinMap[pin] = servoCount; + servoCount++; + } + if (minPulse > 0 && maxPulse > 0) { + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse); + } else { + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin)); + } + } else { + Firmata.sendString("Max servos attached"); + } +} + +void detachServo(byte pin) +{ + servos[servoPinMap[pin]].detach(); + // if we're detaching the last servo, decrement the count + // otherwise store the index of the detached servo + if (servoPinMap[pin] == servoCount && servoCount > 0) { + servoCount--; + } else if (servoCount > 0) { + // keep track of detached servos because we want to reuse their indexes + // before incrementing the count of attached servos + detachedServoCount++; + detachedServos[detachedServoCount - 1] = servoPinMap[pin]; + } + + servoPinMap[pin] = 255; +} + void readAndReportData(byte address, int theRegister, byte numBytes) { // allow I2C requests that don't require a register read // for example, some devices using an interrupt pin to signify new data available // do not always require the register read so upon interrupt you call Wire.requestFrom() if (theRegister != REGISTER_NOT_SPECIFIED) { Wire.beginTransmission(address); -#if ARDUINO >= 100 - Wire.write((byte)theRegister); -#else - Wire.send((byte)theRegister); -#endif + wireWrite((byte)theRegister); Wire.endTransmission(); // do not set a value of 0 if (i2cReadDelayTime > 0) { @@ -111,23 +166,17 @@ void readAndReportData(byte address, int theRegister, byte numBytes) { Wire.requestFrom(address, numBytes); // all bytes are returned in requestFrom // check to be sure correct number of bytes were returned by slave - if (numBytes == Wire.available()) { - i2cRxData[0] = address; - i2cRxData[1] = theRegister; - for (int i = 0; i < numBytes; i++) { -#if ARDUINO >= 100 - i2cRxData[2 + i] = Wire.read(); -#else - i2cRxData[2 + i] = Wire.receive(); -#endif - } + if (numBytes < Wire.available()) { + Firmata.sendString("I2C: Too many bytes received"); + } else if (numBytes > Wire.available()) { + Firmata.sendString("I2C: Too few bytes received"); } - else { - if (numBytes > Wire.available()) { - Firmata.sendString("I2C Read Error: Too many bytes received"); - } else { - Firmata.sendString("I2C Read Error: Too few bytes received"); - } + + i2cRxData[0] = address; + i2cRxData[1] = theRegister; + + for (int i = 0; i < numBytes && Wire.available(); i++) { + i2cRxData[2 + i] = wireRead(); } // send slave address, register and received bytes @@ -177,13 +226,18 @@ void checkDigitalInputs(void) */ void setPinModeCallback(byte pin, int mode) { + if (pinConfig[pin] == IGNORE) + return; + if (pinConfig[pin] == I2C && isI2CEnabled && mode != I2C) { // disable i2c so pins can be used for other functions // the following if statements should reconfigure the pins properly disableI2CPins(); } - if (IS_PIN_SERVO(pin) && mode != SERVO && servos[PIN_TO_SERVO(pin)].attached()) { - servos[PIN_TO_SERVO(pin)].detach(); + if (IS_PIN_DIGITAL(pin) && mode != SERVO) { + if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { + detachServo(pin); + } } if (IS_PIN_ANALOG(pin)) { reportAnalogCallback(PIN_TO_ANALOG(pin), mode == ANALOG ? 1 : 0); // turn on/off reporting @@ -200,7 +254,7 @@ void setPinModeCallback(byte pin, int mode) case ANALOG: if (IS_PIN_ANALOG(pin)) { if (IS_PIN_DIGITAL(pin)) { - pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups } pinConfig[pin] = ANALOG; @@ -208,7 +262,7 @@ void setPinModeCallback(byte pin, int mode) break; case INPUT: if (IS_PIN_DIGITAL(pin)) { - pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups pinConfig[pin] = INPUT; } @@ -228,10 +282,12 @@ void setPinModeCallback(byte pin, int mode) } break; case SERVO: - if (IS_PIN_SERVO(pin)) { + if (IS_PIN_DIGITAL(pin)) { pinConfig[pin] = SERVO; - if (!servos[PIN_TO_SERVO(pin)].attached()) { - servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin)); + if (servoPinMap[pin] == 255 || !servos[servoPinMap[pin]].attached()) { + // pass -1 for min and max pulse values to use default values set + // by Servo library + attachServo(pin, -1, -1); } } break; @@ -253,8 +309,8 @@ void analogWriteCallback(byte pin, int value) if (pin < TOTAL_PINS) { switch (pinConfig[pin]) { case SERVO: - if (IS_PIN_SERVO(pin)) - servos[PIN_TO_SERVO(pin)].write(value); + if (IS_PIN_DIGITAL(pin)) + servos[servoPinMap[pin]].write(value); pinState[pin] = value; break; case PWM: @@ -300,9 +356,17 @@ void reportAnalogCallback(byte analogPin, int value) { if (analogPin < TOTAL_ANALOG_PINS) { if (value == 0) { - analogInputsToReport = analogInputsToReport &~ (1 << analogPin); + analogInputsToReport = analogInputsToReport & ~ (1 << analogPin); } else { analogInputsToReport = analogInputsToReport | (1 << analogPin); + // prevent during system reset or all analog pin values will be reported + // which may report noise for unconnected analog pins + if (!isResetting) { + // Send pin value immediately. This is helpful when connected via + // ethernet, wi-fi or bluetooth so pin states can be known upon + // reconnecting. + Firmata.sendAnalog(analogPin, analogRead(analogPin)); + } } } // TODO: save status to EEPROM here, if changed @@ -312,6 +376,10 @@ void reportDigitalCallback(byte port, int value) { if (port < TOTAL_PORTS) { reportPINs[port] = (byte)value; + // Send port value immediately. This is helpful when connected via + // ethernet, wi-fi or bluetooth so pin states can be known upon + // reconnecting. + if (value) outputPort(port, readPort(port, portConfigInputs[port]), true); } // do not disable analog reporting on these 8 pins, to allow some // pins used for digital, others analog. Instead, allow both types @@ -329,15 +397,15 @@ void sysexCallback(byte command, byte argc, byte *argv) { byte mode; byte slaveAddress; - byte slaveRegister; byte data; + int slaveRegister; unsigned int delayTime; switch (command) { case I2C_REQUEST: mode = argv[1] & I2C_READ_WRITE_MODE_MASK; if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) { - Firmata.sendString("10-bit addressing mode is not yet supported"); + Firmata.sendString("10-bit addressing not supported"); return; } else { @@ -349,11 +417,7 @@ void sysexCallback(byte command, byte argc, byte *argv) Wire.beginTransmission(slaveAddress); for (byte i = 2; i < argc; i += 2) { data = argv[i] + (argv[i + 1] << 7); -#if ARDUINO >= 100 - Wire.write(data); -#else - Wire.send(data); -#endif + wireWrite(data); } Wire.endTransmission(); delayMicroseconds(70); @@ -363,13 +427,13 @@ void sysexCallback(byte command, byte argc, byte *argv) // a slave register is specified slaveRegister = argv[2] + (argv[3] << 7); data = argv[4] + (argv[5] << 7); // bytes to read - readAndReportData(slaveAddress, (int)slaveRegister, data); } else { // a slave register is NOT specified + slaveRegister = REGISTER_NOT_SPECIFIED; data = argv[2] + (argv[3] << 7); // bytes to read - readAndReportData(slaveAddress, (int)REGISTER_NOT_SPECIFIED, data); } + readAndReportData(slaveAddress, (int)slaveRegister, data); break; case I2C_READ_CONTINUOUSLY: if ((queryIndex + 1) >= MAX_QUERIES) { @@ -377,10 +441,20 @@ void sysexCallback(byte command, byte argc, byte *argv) Firmata.sendString("too many queries"); break; } + if (argc == 6) { + // a slave register is specified + slaveRegister = argv[2] + (argv[3] << 7); + data = argv[4] + (argv[5] << 7); // bytes to read + } + else { + // a slave register is NOT specified + slaveRegister = (int)REGISTER_NOT_SPECIFIED; + data = argv[2] + (argv[3] << 7); // bytes to read + } queryIndex++; query[queryIndex].addr = slaveAddress; - query[queryIndex].reg = argv[2] + (argv[3] << 7); - query[queryIndex].bytes = argv[4] + (argv[5] << 7); + query[queryIndex].reg = slaveRegister; + query[queryIndex].bytes = data; break; case I2C_STOP_READING: byte queryIndexToSkip; @@ -393,7 +467,7 @@ void sysexCallback(byte command, byte argc, byte *argv) // determine which device to stop reading and remove it's data from // the array, shifiting other array data to fill the space for (byte i = 0; i < queryIndex + 1; i++) { - if (query[i].addr = slaveAddress) { + if (query[i].addr == slaveAddress) { queryIndexToSkip = i; break; } @@ -402,7 +476,7 @@ void sysexCallback(byte command, byte argc, byte *argv) for (byte i = queryIndexToSkip; i < queryIndex + 1; i++) { if (i < MAX_QUERIES) { query[i].addr = query[i + 1].addr; - query[i].reg = query[i + 1].addr; + query[i].reg = query[i + 1].reg; query[i].bytes = query[i + 1].bytes; } } @@ -432,10 +506,11 @@ void sysexCallback(byte command, byte argc, byte *argv) int minPulse = argv[1] + (argv[2] << 7); int maxPulse = argv[3] + (argv[4] << 7); - if (IS_PIN_SERVO(pin)) { - if (servos[PIN_TO_SERVO(pin)].attached()) - servos[PIN_TO_SERVO(pin)].detach(); - servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse); + if (IS_PIN_DIGITAL(pin)) { + if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { + detachServo(pin); + } + attachServo(pin, minPulse, maxPulse); setPinModeCallback(pin, SERVO); } } @@ -470,19 +545,19 @@ void sysexCallback(byte command, byte argc, byte *argv) } if (IS_PIN_ANALOG(pin)) { Firmata.write(ANALOG); - Firmata.write(10); + Firmata.write(10); // 10 = 10-bit resolution } if (IS_PIN_PWM(pin)) { Firmata.write(PWM); - Firmata.write(8); + Firmata.write(8); // 8 = 8-bit resolution } - if (IS_PIN_SERVO(pin)) { + if (IS_PIN_DIGITAL(pin)) { Firmata.write(SERVO); Firmata.write(14); } if (IS_PIN_I2C(pin)) { Firmata.write(I2C); - Firmata.write(1); // to do: determine appropriate value + Firmata.write(1); // TODO: could assign a number to map to SCL or SDA } Firmata.write(127); } @@ -528,7 +603,6 @@ void enableI2CPins() isI2CEnabled = true; - // is there enough time before the first I2C request to call this here? Wire.begin(); } @@ -537,8 +611,6 @@ void disableI2CPins() { isI2CEnabled = false; // disable read continuous mode for all devices queryIndex = -1; - // uncomment the following if or when the end() method is added to Wire library - // Wire.end(); } /*============================================================================== @@ -547,19 +619,24 @@ void disableI2CPins() { void systemResetCallback() { + isResetting = true; + // initialize a defalt state // TODO: option to load config from EEPROM instead of default + if (isI2CEnabled) { disableI2CPins(); } + for (byte i = 0; i < TOTAL_PORTS; i++) { - reportPINs[i] = false; // by default, reporting off - portConfigInputs[i] = 0; // until activated + reportPINs[i] = false; // by default, reporting off + portConfigInputs[i] = 0; // until activated previousPINs[i] = 0; } - // pins with analog capability default to analog input - // otherwise, pins default to digital output + for (byte i = 0; i < TOTAL_PINS; i++) { + // pins with analog capability default to analog input + // otherwise, pins default to digital output if (IS_PIN_ANALOG(i)) { // turns off pullup, configures everything setPinModeCallback(i, ANALOG); @@ -567,10 +644,15 @@ void systemResetCallback() // sets the output to 0, configures portConfigInputs setPinModeCallback(i, OUTPUT); } + + servoPinMap[i] = 255; } // by default, do not report any analog inputs analogInputsToReport = 0; + detachedServoCount = 0; + servoCount = 0; + /* send digital inputs to set the initial state on the host computer, * since once in the loop(), this firmware will only send on change */ /* @@ -580,6 +662,7 @@ void systemResetCallback() outputPort(i, readPort(i, portConfigInputs[i]), true); } */ + isResetting = false; } void setup() @@ -609,14 +692,12 @@ void loop() * FTDI buffer using Serial.print() */ checkDigitalInputs(); - /* SERIALREAD - processing incoming messagse as soon as possible, while still + /* STREAMREAD - processing incoming messagse as soon as possible, while still * checking digital inputs. */ while (Firmata.available()) Firmata.processInput(); - /* SEND FTDI WRITE BUFFER - make sure that the FTDI buffer doesn't go over - * 60 bytes. use a timer to sending an event character every 4 ms to - * trigger the buffer to dump. */ + // TODO - ensure that Stream buffer doesn't go over 60 bytes currentMillis = millis(); if (currentMillis - previousMillis > samplingInterval) { diff --git a/libraries/Firmata/examples/StandardFirmataEthernet/StandardFirmataEthernet.ino b/libraries/Firmata/examples/StandardFirmataEthernet/StandardFirmataEthernet.ino new file mode 100644 index 00000000000..cc5fdb0fb28 --- /dev/null +++ b/libraries/Firmata/examples/StandardFirmataEthernet/StandardFirmataEthernet.ino @@ -0,0 +1,883 @@ +/* + Firmata is a generic protocol for communicating with microcontrollers + from software on a host computer. It is intended to work with + any host computer software package. + + To download a host software package, please clink on the following link + to open the list of Firmata client libraries your default browser. + + https://github.com/firmata/arduino#firmata-client-libraries + + Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved. + Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved. + Copyright (C) 2009 Shigeru Kobayashi. All rights reserved. + Copyright (C) 2009-2015 Jeff Hoefs. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + See file LICENSE.txt for further informations on licensing terms. + + Last updated by Jeff Hoefs: April 11, 2015 +*/ + +/* + README + + To use StandardFirmataEthernet you will need to have one of the following + boards or shields: + + - Arduino Ethernet shield (or clone) + - Arduino Ethernet board (or clone) + - Arduino Yun + + Follow the instructions in the NETWORK CONFIGURATION section below to + configure your particular hardware. + + NOTE: If you are using an Arduino Ethernet shield you cannot use the following pins on + the following boards. Firmata will ignore any requests to use these pins: + + - Arduino Uno or other ATMega328 boards: (D4, D10, D11, D12, D13) + - Arduino Mega: (D4, D10, D50, D51, D52, D53) + - Arduino Leonardo: (D4, D10) + - Arduino Due: (D4, D10) +*/ + +#include +#include +#include + +//#define SERIAL_DEBUG +#include "utility/firmataDebug.h" + +#define I2C_WRITE B00000000 +#define I2C_READ B00001000 +#define I2C_READ_CONTINUOUSLY B00010000 +#define I2C_STOP_READING B00011000 +#define I2C_READ_WRITE_MODE_MASK B00011000 +#define I2C_10BIT_ADDRESS_MODE_MASK B00100000 +#define I2C_MAX_QUERIES 8 +#define I2C_REGISTER_NOT_SPECIFIED -1 + +// the minimum interval for sampling analog input +#define MINIMUM_SAMPLING_INTERVAL 10 + + +/*============================================================================== + * NETWORK CONFIGURATION + * + * You must configure your particular hardware. Follow the steps below. + *============================================================================*/ + +// STEP 1 [REQUIRED] +// Uncomment / comment the appropriate set of includes for your hardware (OPTION A or B) +// Option A is enabled by default. + +/* + * OPTION A: Configure for Arduino Ethernet board or shield + * + * To configure StandardFirmataEthernet to use the original WIZ5100-based + * ethernet shield or Arduino Ethernet uncomment the includes of 'SPI.h' and 'Ethernet.h': + */ + +#include +#include + +/* + * OPTION B: Configure for Arduin Yun + * + * To execute StandardFirmataEthernet on Yun uncomment Bridge.h and YunClient.h. + * Do not include Ethernet.h or SPI.h above in this case. + * On Yun there's no need to configure local_ip and mac in the sketch + * as this is configured on the linux-side of Yun. + */ + +// #include +// #include + + +// STEP 2 [REQUIRED for all boards and shields] +// replace with IP of the server you want to connect to, comment out if using 'remote_host' +#define remote_ip IPAddress(10, 0, 0, 3) +// *** REMOTE HOST IS NOT YET WORKING *** +// replace with hostname of server you want to connect to, comment out if using 'remote_ip' +// #define remote_host "server.local" + +// STEP 3 [REQUIRED unless using Arduin Yun] +// Replace with the port that your server is listening on +#define remote_port 3030 + +// STEP 4 [REQUIRED unless using Arduino Yun OR if not using DHCP] +// Replace with your board or ethernet shield's IP address +// Comment out if you want to use DHCP +#define local_ip IPAddress(10, 0, 0, 15) + +// STEP 5 [REQUIRED unless using Arduino Yun] +// replace with ethernet shield mac. Must be unique for your network +const byte mac[] = {0x90, 0xA2, 0xDA, 0x00, 0x53, 0xE5}; + +#if !defined ethernet_h && !defined _YUN_CLIENT_H_ +#error "you must uncomment the includes for your board configuration. See OPTIONS A and B in the NETWORK CONFIGURATION SECTION" +#endif + +#if defined remote_ip && defined remote_host +#error "cannot define both remote_ip and remote_host at the same time!" +#endif + + +/*============================================================================== + * GLOBAL VARIABLES + *============================================================================*/ + +/* network */ + +#include "utility/EthernetClientStream.h" + +#ifdef _YUN_CLIENT_H_ +YunClient client; +#else +EthernetClient client; +#endif + +#if defined remote_ip && !defined remote_host +#ifdef local_ip +EthernetClientStream stream(client, local_ip, remote_ip, NULL, remote_port); +#else +EthernetClientStream stream(client, IPAddress(0, 0, 0, 0), remote_ip, NULL, remote_port); +#endif +#endif + +#if !defined remote_ip && defined remote_host +#ifdef local_ip +EthernetClientStream stream(client, local_ip, IPAddress(0, 0, 0, 0), remote_host, remote_port); +#else +EthernetClientStream stream(client, IPAddress(0, 0, 0, 0), IPAddress(0, 0, 0, 0), remote_host, remote_port); +#endif +#endif + +/* analog inputs */ +int analogInputsToReport = 0; // bitwise array to store pin reporting + +/* digital input ports */ +byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence +byte previousPINs[TOTAL_PORTS]; // previous 8 bits sent + +/* pins configuration */ +byte pinConfig[TOTAL_PINS]; // configuration of every pin +byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else +int pinState[TOTAL_PINS]; // any value that has been written + +/* timer variables */ +unsigned long currentMillis; // store the current value from millis() +unsigned long previousMillis; // for comparison with currentMillis +unsigned int samplingInterval = 19; // how often to sample analog inputs (in ms) + +/* i2c data */ +struct i2c_device_info { + byte addr; + int reg; + byte bytes; +}; + +/* for i2c read continuous mode */ +i2c_device_info query[I2C_MAX_QUERIES]; + +byte i2cRxData[32]; +boolean isI2CEnabled = false; +signed char queryIndex = -1; +// default delay time between i2c read request and Wire.requestFrom() +unsigned int i2cReadDelayTime = 0; + +Servo servos[MAX_SERVOS]; +byte servoPinMap[TOTAL_PINS]; +byte detachedServos[MAX_SERVOS]; +byte detachedServoCount = 0; +byte servoCount = 0; + +boolean isResetting = false; + +/* utility functions */ +void wireWrite(byte data) +{ +#if ARDUINO >= 100 + Wire.write((byte)data); +#else + Wire.send(data); +#endif +} + +byte wireRead(void) +{ +#if ARDUINO >= 100 + return Wire.read(); +#else + return Wire.receive(); +#endif +} + +/*============================================================================== + * FUNCTIONS + *============================================================================*/ + +void attachServo(byte pin, int minPulse, int maxPulse) +{ + if (servoCount < MAX_SERVOS) { + // reuse indexes of detached servos until all have been reallocated + if (detachedServoCount > 0) { + servoPinMap[pin] = detachedServos[detachedServoCount - 1]; + if (detachedServoCount > 0) detachedServoCount--; + } else { + servoPinMap[pin] = servoCount; + servoCount++; + } + if (minPulse > 0 && maxPulse > 0) { + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse); + } else { + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin)); + } + } else { + Firmata.sendString("Max servos attached"); + } +} + +void detachServo(byte pin) +{ + servos[servoPinMap[pin]].detach(); + // if we're detaching the last servo, decrement the count + // otherwise store the index of the detached servo + if (servoPinMap[pin] == servoCount && servoCount > 0) { + servoCount--; + } else if (servoCount > 0) { + // keep track of detached servos because we want to reuse their indexes + // before incrementing the count of attached servos + detachedServoCount++; + detachedServos[detachedServoCount - 1] = servoPinMap[pin]; + } + + servoPinMap[pin] = 255; +} + +void readAndReportData(byte address, int theRegister, byte numBytes) { + // allow I2C requests that don't require a register read + // for example, some devices using an interrupt pin to signify new data available + // do not always require the register read so upon interrupt you call Wire.requestFrom() + if (theRegister != I2C_REGISTER_NOT_SPECIFIED) { + Wire.beginTransmission(address); + wireWrite((byte)theRegister); + Wire.endTransmission(); + // do not set a value of 0 + if (i2cReadDelayTime > 0) { + // delay is necessary for some devices such as WiiNunchuck + delayMicroseconds(i2cReadDelayTime); + } + } else { + theRegister = 0; // fill the register with a dummy value + } + + Wire.requestFrom(address, numBytes); // all bytes are returned in requestFrom + + // check to be sure correct number of bytes were returned by slave + if (numBytes < Wire.available()) { + Firmata.sendString("I2C: Too many bytes received"); + } else if (numBytes > Wire.available()) { + Firmata.sendString("I2C: Too few bytes received"); + } + + i2cRxData[0] = address; + i2cRxData[1] = theRegister; + + for (int i = 0; i < numBytes && Wire.available(); i++) { + i2cRxData[2 + i] = wireRead(); + } + + // send slave address, register and received bytes + Firmata.sendSysex(SYSEX_I2C_REPLY, numBytes + 2, i2cRxData); +} + +void outputPort(byte portNumber, byte portValue, byte forceSend) +{ + // pins not configured as INPUT are cleared to zeros + portValue = portValue & portConfigInputs[portNumber]; + // only send if the value is different than previously sent + if (forceSend || previousPINs[portNumber] != portValue) { + Firmata.sendDigitalPort(portNumber, portValue); + previousPINs[portNumber] = portValue; + } +} + +/* ----------------------------------------------------------------------------- + * check all the active digital inputs for change of state, then add any events + * to the Stream output queue using Stream.write() */ +void checkDigitalInputs(void) +{ + /* Using non-looping code allows constants to be given to readPort(). + * The compiler will apply substantial optimizations if the inputs + * to readPort() are compile-time constants. */ + if (TOTAL_PORTS > 0 && reportPINs[0]) outputPort(0, readPort(0, portConfigInputs[0]), false); + if (TOTAL_PORTS > 1 && reportPINs[1]) outputPort(1, readPort(1, portConfigInputs[1]), false); + if (TOTAL_PORTS > 2 && reportPINs[2]) outputPort(2, readPort(2, portConfigInputs[2]), false); + if (TOTAL_PORTS > 3 && reportPINs[3]) outputPort(3, readPort(3, portConfigInputs[3]), false); + if (TOTAL_PORTS > 4 && reportPINs[4]) outputPort(4, readPort(4, portConfigInputs[4]), false); + if (TOTAL_PORTS > 5 && reportPINs[5]) outputPort(5, readPort(5, portConfigInputs[5]), false); + if (TOTAL_PORTS > 6 && reportPINs[6]) outputPort(6, readPort(6, portConfigInputs[6]), false); + if (TOTAL_PORTS > 7 && reportPINs[7]) outputPort(7, readPort(7, portConfigInputs[7]), false); + if (TOTAL_PORTS > 8 && reportPINs[8]) outputPort(8, readPort(8, portConfigInputs[8]), false); + if (TOTAL_PORTS > 9 && reportPINs[9]) outputPort(9, readPort(9, portConfigInputs[9]), false); + if (TOTAL_PORTS > 10 && reportPINs[10]) outputPort(10, readPort(10, portConfigInputs[10]), false); + if (TOTAL_PORTS > 11 && reportPINs[11]) outputPort(11, readPort(11, portConfigInputs[11]), false); + if (TOTAL_PORTS > 12 && reportPINs[12]) outputPort(12, readPort(12, portConfigInputs[12]), false); + if (TOTAL_PORTS > 13 && reportPINs[13]) outputPort(13, readPort(13, portConfigInputs[13]), false); + if (TOTAL_PORTS > 14 && reportPINs[14]) outputPort(14, readPort(14, portConfigInputs[14]), false); + if (TOTAL_PORTS > 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false); +} + +// ----------------------------------------------------------------------------- +/* sets the pin mode to the correct state and sets the relevant bits in the + * two bit-arrays that track Digital I/O and PWM status + */ +void setPinModeCallback(byte pin, int mode) +{ + if (pinConfig[pin] == IGNORE) + return; + + if (pinConfig[pin] == I2C && isI2CEnabled && mode != I2C) { + // disable i2c so pins can be used for other functions + // the following if statements should reconfigure the pins properly + disableI2CPins(); + } + if (IS_PIN_DIGITAL(pin) && mode != SERVO) { + if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { + detachServo(pin); + } + } + if (IS_PIN_ANALOG(pin)) { + reportAnalogCallback(PIN_TO_ANALOG(pin), mode == ANALOG ? 1 : 0); // turn on/off reporting + } + if (IS_PIN_DIGITAL(pin)) { + if (mode == INPUT) { + portConfigInputs[pin / 8] |= (1 << (pin & 7)); + } else { + portConfigInputs[pin / 8] &= ~(1 << (pin & 7)); + } + } + pinState[pin] = 0; + switch (mode) { + case ANALOG: + if (IS_PIN_ANALOG(pin)) { + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups + } + pinConfig[pin] = ANALOG; + } + break; + case INPUT: + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups + pinConfig[pin] = INPUT; + } + break; + case OUTPUT: + if (IS_PIN_DIGITAL(pin)) { + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable PWM + pinMode(PIN_TO_DIGITAL(pin), OUTPUT); + pinConfig[pin] = OUTPUT; + } + break; + case PWM: + if (IS_PIN_PWM(pin)) { + pinMode(PIN_TO_PWM(pin), OUTPUT); + analogWrite(PIN_TO_PWM(pin), 0); + pinConfig[pin] = PWM; + } + break; + case SERVO: + if (IS_PIN_DIGITAL(pin)) { + pinConfig[pin] = SERVO; + if (servoPinMap[pin] == 255 || !servos[servoPinMap[pin]].attached()) { + // pass -1 for min and max pulse values to use default values set + // by Servo library + attachServo(pin, -1, -1); + } + } + break; + case I2C: + if (IS_PIN_I2C(pin)) { + // mark the pin as i2c + // the user must call I2C_CONFIG to enable I2C for a device + pinConfig[pin] = I2C; + } + break; + default: + Firmata.sendString("Unknown pin mode"); // TODO: put error msgs in EEPROM + } + // TODO: save status to EEPROM here, if changed +} + +void analogWriteCallback(byte pin, int value) +{ + if (pin < TOTAL_PINS) { + switch (pinConfig[pin]) { + case SERVO: + if (IS_PIN_DIGITAL(pin)) + servos[servoPinMap[pin]].write(value); + pinState[pin] = value; + break; + case PWM: + if (IS_PIN_PWM(pin)) + analogWrite(PIN_TO_PWM(pin), value); + pinState[pin] = value; + break; + } + } +} + +void digitalWriteCallback(byte port, int value) +{ + byte pin, lastPin, mask = 1, pinWriteMask = 0; + + if (port < TOTAL_PORTS) { + // create a mask of the pins on this port that are writable. + lastPin = port * 8 + 8; + if (lastPin > TOTAL_PINS) lastPin = TOTAL_PINS; + for (pin = port * 8; pin < lastPin; pin++) { + // do not disturb non-digital pins (eg, Rx & Tx) + if (IS_PIN_DIGITAL(pin)) { + // only write to OUTPUT and INPUT (enables pullup) + // do not touch pins in PWM, ANALOG, SERVO or other modes + if (pinConfig[pin] == OUTPUT || pinConfig[pin] == INPUT) { + pinWriteMask |= mask; + pinState[pin] = ((byte)value & mask) ? 1 : 0; + } + } + mask = mask << 1; + } + writePort(port, (byte)value, pinWriteMask); + } +} + + +// ----------------------------------------------------------------------------- +/* sets bits in a bit array (int) to toggle the reporting of the analogIns + */ +//void FirmataClass::setAnalogPinReporting(byte pin, byte state) { +//} +void reportAnalogCallback(byte analogPin, int value) +{ + if (analogPin < TOTAL_ANALOG_PINS) { + if (value == 0) { + analogInputsToReport = analogInputsToReport & ~ (1 << analogPin); + } else { + analogInputsToReport = analogInputsToReport | (1 << analogPin); + // prevent during system reset or all analog pin values will be reported + // which may report noise for unconnected analog pins + if (!isResetting) { + // Send pin value immediately. This is helpful when connected via + // ethernet, wi-fi or bluetooth so pin states can be known upon + // reconnecting. + Firmata.sendAnalog(analogPin, analogRead(analogPin)); + } + } + } + // TODO: save status to EEPROM here, if changed +} + +void reportDigitalCallback(byte port, int value) +{ + if (port < TOTAL_PORTS) { + reportPINs[port] = (byte)value; + // Send port value immediately. This is helpful when connected via + // ethernet, wi-fi or bluetooth so pin states can be known upon + // reconnecting. + if (value) outputPort(port, readPort(port, portConfigInputs[port]), true); + } + // do not disable analog reporting on these 8 pins, to allow some + // pins used for digital, others analog. Instead, allow both types + // of reporting to be enabled, but check if the pin is configured + // as analog when sampling the analog inputs. Likewise, while + // scanning digital pins, portConfigInputs will mask off values from any + // pins configured as analog +} + +/*============================================================================== + * SYSEX-BASED commands + *============================================================================*/ + +void sysexCallback(byte command, byte argc, byte *argv) +{ + byte mode; + byte slaveAddress; + byte data; + int slaveRegister; + unsigned int delayTime; + + switch (command) { + case I2C_REQUEST: + mode = argv[1] & I2C_READ_WRITE_MODE_MASK; + if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) { + Firmata.sendString("10-bit addressing not supported"); + return; + } + else { + slaveAddress = argv[0]; + } + + switch (mode) { + case I2C_WRITE: + Wire.beginTransmission(slaveAddress); + for (byte i = 2; i < argc; i += 2) { + data = argv[i] + (argv[i + 1] << 7); + wireWrite(data); + } + Wire.endTransmission(); + delayMicroseconds(70); + break; + case I2C_READ: + if (argc == 6) { + // a slave register is specified + slaveRegister = argv[2] + (argv[3] << 7); + data = argv[4] + (argv[5] << 7); // bytes to read + } + else { + // a slave register is NOT specified + slaveRegister = I2C_REGISTER_NOT_SPECIFIED; + data = argv[2] + (argv[3] << 7); // bytes to read + } + readAndReportData(slaveAddress, (int)slaveRegister, data); + break; + case I2C_READ_CONTINUOUSLY: + if ((queryIndex + 1) >= I2C_MAX_QUERIES) { + // too many queries, just ignore + Firmata.sendString("too many I2C queries"); + break; + } + if (argc == 6) { + // a slave register is specified + slaveRegister = argv[2] + (argv[3] << 7); + data = argv[4] + (argv[5] << 7); // bytes to read + } + else { + // a slave register is NOT specified + slaveRegister = (int)I2C_REGISTER_NOT_SPECIFIED; + data = argv[2] + (argv[3] << 7); // bytes to read + } + queryIndex++; + query[queryIndex].addr = slaveAddress; + query[queryIndex].reg = slaveRegister; + query[queryIndex].bytes = data; + break; + case I2C_STOP_READING: + byte queryIndexToSkip; + // if read continuous mode is enabled for only 1 i2c device, disable + // read continuous reporting for that device + if (queryIndex <= 0) { + queryIndex = -1; + } else { + // if read continuous mode is enabled for multiple devices, + // determine which device to stop reading and remove it's data from + // the array, shifiting other array data to fill the space + for (byte i = 0; i < queryIndex + 1; i++) { + if (query[i].addr == slaveAddress) { + queryIndexToSkip = i; + break; + } + } + + for (byte i = queryIndexToSkip; i < queryIndex + 1; i++) { + if (i < I2C_MAX_QUERIES) { + query[i].addr = query[i + 1].addr; + query[i].reg = query[i + 1].reg; + query[i].bytes = query[i + 1].bytes; + } + } + queryIndex--; + } + break; + default: + break; + } + break; + case I2C_CONFIG: + delayTime = (argv[0] + (argv[1] << 7)); + + if (delayTime > 0) { + i2cReadDelayTime = delayTime; + } + + if (!isI2CEnabled) { + enableI2CPins(); + } + + break; + case SERVO_CONFIG: + if (argc > 4) { + // these vars are here for clarity, they'll optimized away by the compiler + byte pin = argv[0]; + int minPulse = argv[1] + (argv[2] << 7); + int maxPulse = argv[3] + (argv[4] << 7); + + if (IS_PIN_DIGITAL(pin)) { + if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { + detachServo(pin); + } + attachServo(pin, minPulse, maxPulse); + setPinModeCallback(pin, SERVO); + } + } + break; + case SAMPLING_INTERVAL: + if (argc > 1) { + samplingInterval = argv[0] + (argv[1] << 7); + if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) { + samplingInterval = MINIMUM_SAMPLING_INTERVAL; + } + } else { + //Firmata.sendString("Not enough data"); + } + break; + case EXTENDED_ANALOG: + if (argc > 1) { + int val = argv[1]; + if (argc > 2) val |= (argv[2] << 7); + if (argc > 3) val |= (argv[3] << 14); + analogWriteCallback(argv[0], val); + } + break; + case CAPABILITY_QUERY: + Firmata.write(START_SYSEX); + Firmata.write(CAPABILITY_RESPONSE); + for (byte pin = 0; pin < TOTAL_PINS; pin++) { + if (IS_PIN_DIGITAL(pin)) { + Firmata.write((byte)INPUT); + Firmata.write(1); + Firmata.write((byte)OUTPUT); + Firmata.write(1); + } + if (IS_PIN_ANALOG(pin)) { + Firmata.write(ANALOG); + Firmata.write(10); // 10 = 10-bit resolution + } + if (IS_PIN_PWM(pin)) { + Firmata.write(PWM); + Firmata.write(8); // 8 = 8-bit resolution + } + if (IS_PIN_DIGITAL(pin)) { + Firmata.write(SERVO); + Firmata.write(14); + } + if (IS_PIN_I2C(pin)) { + Firmata.write(I2C); + Firmata.write(1); // TODO: could assign a number to map to SCL or SDA + } + Firmata.write(127); + } + Firmata.write(END_SYSEX); + break; + case PIN_STATE_QUERY: + if (argc > 0) { + byte pin = argv[0]; + Firmata.write(START_SYSEX); + Firmata.write(PIN_STATE_RESPONSE); + Firmata.write(pin); + if (pin < TOTAL_PINS) { + Firmata.write((byte)pinConfig[pin]); + Firmata.write((byte)pinState[pin] & 0x7F); + if (pinState[pin] & 0xFF80) Firmata.write((byte)(pinState[pin] >> 7) & 0x7F); + if (pinState[pin] & 0xC000) Firmata.write((byte)(pinState[pin] >> 14) & 0x7F); + } + Firmata.write(END_SYSEX); + } + break; + case ANALOG_MAPPING_QUERY: + Firmata.write(START_SYSEX); + Firmata.write(ANALOG_MAPPING_RESPONSE); + for (byte pin = 0; pin < TOTAL_PINS; pin++) { + Firmata.write(IS_PIN_ANALOG(pin) ? PIN_TO_ANALOG(pin) : 127); + } + Firmata.write(END_SYSEX); + break; + } +} + +void enableI2CPins() +{ + byte i; + // is there a faster way to do this? would probaby require importing + // Arduino.h to get SCL and SDA pins + for (i = 0; i < TOTAL_PINS; i++) { + if (IS_PIN_I2C(i)) { + // mark pins as i2c so they are ignore in non i2c data requests + setPinModeCallback(i, I2C); + } + } + + isI2CEnabled = true; + + Wire.begin(); +} + +/* disable the i2c pins so they can be used for other functions */ +void disableI2CPins() { + isI2CEnabled = false; + // disable read continuous mode for all devices + queryIndex = -1; +} + +/*============================================================================== + * SETUP() + *============================================================================*/ + +void systemResetCallback() +{ + isResetting = true; + + // initialize a defalt state + // TODO: option to load config from EEPROM instead of default + + if (isI2CEnabled) { + disableI2CPins(); + } + + for (byte i = 0; i < TOTAL_PORTS; i++) { + reportPINs[i] = false; // by default, reporting off + portConfigInputs[i] = 0; // until activated + previousPINs[i] = 0; + } + + for (byte i = 0; i < TOTAL_PINS; i++) { + // pins with analog capability default to analog input + // otherwise, pins default to digital output + if (IS_PIN_ANALOG(i)) { + // turns off pullup, configures everything + setPinModeCallback(i, ANALOG); + } else { + // sets the output to 0, configures portConfigInputs + setPinModeCallback(i, OUTPUT); + } + + servoPinMap[i] = 255; + } + // by default, do not report any analog inputs + analogInputsToReport = 0; + + detachedServoCount = 0; + servoCount = 0; + + /* send digital inputs to set the initial state on the host computer, + * since once in the loop(), this firmware will only send on change */ + /* + TODO: this can never execute, since no pins default to digital input + but it will be needed when/if we support EEPROM stored config + for (byte i=0; i < TOTAL_PORTS; i++) { + outputPort(i, readPort(i, portConfigInputs[i]), true); + } + */ + isResetting = false; +} + +void setup() +{ + DEBUG_BEGIN(9600); + +#ifdef _YUN_CLIENT_H_ + Bridge.begin(); +#else +#ifdef local_ip + Ethernet.begin((uint8_t *)mac, local_ip); //start ethernet +#else + Ethernet.begin((uint8_t *)mac); //start ethernet using dhcp +#endif +#endif + + DEBUG_PRINTLN("connecting..."); + + Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION); + + Firmata.attach(ANALOG_MESSAGE, analogWriteCallback); + Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback); + Firmata.attach(REPORT_ANALOG, reportAnalogCallback); + Firmata.attach(REPORT_DIGITAL, reportDigitalCallback); + Firmata.attach(SET_PIN_MODE, setPinModeCallback); + Firmata.attach(START_SYSEX, sysexCallback); + Firmata.attach(SYSTEM_RESET, systemResetCallback); + + // StandardFirmataEthernet communicates with Ethernet shields over SPI. Therefor all + // SPI pins must be set to IGNORE. Otherwise Firmata would break SPI communication. + // add Pin 10 and configure pin 53 as output if using a MEGA with an Ethernet shield. + + // ignore SPI and pin 4 that is SS for SD-Card on Ethernet-shield + for (byte i = 0; i < TOTAL_PINS; i++) { + if (IS_PIN_SPI(i) + || 4 == i // SD-Card on Ethernet-shiedl uses pin 4 for SS + || 10 == i // Ethernet-shield uses pin 10 for SS +#if defined(__AVR_ATmega32U4__) + || 24 == i // On Leonardo, pin 24 maps to D4 and pin 28 maps to D10 + || 28 == i +#endif + ) { + pinConfig[i] = IGNORE; + } + } + + // Arduino Ethernet, Arduino EthernetShield and Arduino Yun all have SD SS wired to D4 + pinMode(PIN_TO_DIGITAL(4), OUTPUT); // switch off SD card bypassing Firmata + digitalWrite(PIN_TO_DIGITAL(4), HIGH); // SS is active low; + +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + pinMode(PIN_TO_DIGITAL(53), OUTPUT); // configure hardware SS as output on MEGA +#endif + + // start up Network Firmata: + Firmata.begin(stream); + systemResetCallback(); // reset to default config +} + +/*============================================================================== + * LOOP() + *============================================================================*/ +void loop() +{ + byte pin, analogPin; + + /* DIGITALREAD - as fast as possible, check for changes and output them to the + * Stream buffer using Stream.write() */ + checkDigitalInputs(); + + /* STREAMREAD - processing incoming messagse as soon as possible, while still + * checking digital inputs. */ + while (Firmata.available()) + Firmata.processInput(); + + // TODO - ensure that Stream buffer doesn't go over 60 bytes + + currentMillis = millis(); + if (currentMillis - previousMillis > samplingInterval) { + previousMillis += samplingInterval; + /* ANALOGREAD - do all analogReads() at the configured sampling interval */ + for (pin = 0; pin < TOTAL_PINS; pin++) { + if (IS_PIN_ANALOG(pin) && pinConfig[pin] == ANALOG) { + analogPin = PIN_TO_ANALOG(pin); + if (analogInputsToReport & (1 << analogPin)) { + Firmata.sendAnalog(analogPin, analogRead(analogPin)); + } + } + } + // report i2c data for all device with read continuous mode enabled + if (queryIndex > -1) { + for (byte i = 0; i < queryIndex + 1; i++) { + readAndReportData(query[i].addr, query[i].reg, query[i].bytes); + } + } + } + +#if !defined local_ip && !defined _YUN_CLIENT_H_ + if (Ethernet.maintain()) + { + stream.maintain(Ethernet.localIP()); + } +#endif + +} diff --git a/libraries/Firmata/examples/StandardFirmataYun/StandardFirmataYun.ino b/libraries/Firmata/examples/StandardFirmataYun/StandardFirmataYun.ino new file mode 100644 index 00000000000..9391fb93b61 --- /dev/null +++ b/libraries/Firmata/examples/StandardFirmataYun/StandardFirmataYun.ino @@ -0,0 +1,745 @@ +/* + Firmata is a generic protocol for communicating with microcontrollers + from software on a host computer. It is intended to work with + any host computer software package. + + To download a host software package, please clink on the following link + to open the download page in your default browser. + + https://github.com/firmata/arduino#firmata-client-libraries + + Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved. + Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved. + Copyright (C) 2009 Shigeru Kobayashi. All rights reserved. + Copyright (C) 2009-2014 Jeff Hoefs. All rights reserved. + Copyright (C) 2014 Alan Yorinks. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + See file LICENSE.txt for further informations on licensing terms. + + Last updated by Jeff Hoefs: April 11, 2015 + */ + +/* + README + + The Arduino Yun has both an Arduino (Atmega32u4) environment and a + Linux (Linino) environment. + + StandardFirmataYun enables Firmata running on the Arduino environment + to communicate with a Firmata client application running on the Linux + environment. +*/ + +#include +#include +#include + +#define I2C_WRITE B00000000 +#define I2C_READ B00001000 +#define I2C_READ_CONTINUOUSLY B00010000 +#define I2C_STOP_READING B00011000 +#define I2C_READ_WRITE_MODE_MASK B00011000 +#define I2C_10BIT_ADDRESS_MODE_MASK B00100000 +#define MAX_QUERIES 8 +#define REGISTER_NOT_SPECIFIED -1 + +// the minimum interval for sampling analog input +#define MINIMUM_SAMPLING_INTERVAL 10 + + +/*============================================================================== + * GLOBAL VARIABLES + *============================================================================*/ + +/* analog inputs */ +int analogInputsToReport = 0; // bitwise array to store pin reporting + +/* digital input ports */ +byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence +byte previousPINs[TOTAL_PORTS]; // previous 8 bits sent + +/* pins configuration */ +byte pinConfig[TOTAL_PINS]; // configuration of every pin +byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else +int pinState[TOTAL_PINS]; // any value that has been written + +/* timer variables */ +unsigned long currentMillis; // store the current value from millis() +unsigned long previousMillis; // for comparison with currentMillis +unsigned int samplingInterval = 19; // how often to run the main loop (in ms) + +/* i2c data */ +struct i2c_device_info { + byte addr; + int reg; + byte bytes; +}; + +/* for i2c read continuous more */ +i2c_device_info query[MAX_QUERIES]; + +byte i2cRxData[32]; +boolean isI2CEnabled = false; +signed char queryIndex = -1; +// default delay time between i2c read request and Wire.requestFrom() +unsigned int i2cReadDelayTime = 0; + +Servo servos[MAX_SERVOS]; +byte servoPinMap[TOTAL_PINS]; +byte detachedServos[MAX_SERVOS]; +byte detachedServoCount = 0; +byte servoCount = 0; + +boolean isResetting = false; + +/* utility functions */ +void wireWrite(byte data) +{ +#if ARDUINO >= 100 + Wire.write((byte)data); +#else + Wire.send(data); +#endif +} + +byte wireRead(void) +{ +#if ARDUINO >= 100 + return Wire.read(); +#else + return Wire.receive(); +#endif +} + +/*============================================================================== + * FUNCTIONS + *============================================================================*/ + +void attachServo(byte pin, int minPulse, int maxPulse) +{ + if (servoCount < MAX_SERVOS) { + // reuse indexes of detached servos until all have been reallocated + if (detachedServoCount > 0) { + servoPinMap[pin] = detachedServos[detachedServoCount - 1]; + if (detachedServoCount > 0) detachedServoCount--; + } else { + servoPinMap[pin] = servoCount; + servoCount++; + } + if (minPulse > 0 && maxPulse > 0) { + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse); + } else { + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin)); + } + } else { + Firmata.sendString("Max servos attached"); + } +} + +void detachServo(byte pin) +{ + servos[servoPinMap[pin]].detach(); + // if we're detaching the last servo, decrement the count + // otherwise store the index of the detached servo + if (servoPinMap[pin] == servoCount && servoCount > 0) { + servoCount--; + } else if (servoCount > 0) { + // keep track of detached servos because we want to reuse their indexes + // before incrementing the count of attached servos + detachedServoCount++; + detachedServos[detachedServoCount - 1] = servoPinMap[pin]; + } + + servoPinMap[pin] = 255; +} + +void readAndReportData(byte address, int theRegister, byte numBytes) { + // allow I2C requests that don't require a register read + // for example, some devices using an interrupt pin to signify new data available + // do not always require the register read so upon interrupt you call Wire.requestFrom() + if (theRegister != REGISTER_NOT_SPECIFIED) { + Wire.beginTransmission(address); + wireWrite((byte)theRegister); + Wire.endTransmission(); + // do not set a value of 0 + if (i2cReadDelayTime > 0) { + // delay is necessary for some devices such as WiiNunchuck + delayMicroseconds(i2cReadDelayTime); + } + } else { + theRegister = 0; // fill the register with a dummy value + } + + Wire.requestFrom(address, numBytes); // all bytes are returned in requestFrom + + // check to be sure correct number of bytes were returned by slave + if (numBytes < Wire.available()) { + Firmata.sendString("I2C: Too many bytes received"); + } else if (numBytes > Wire.available()) { + Firmata.sendString("I2C: Too few bytes received"); + } + + i2cRxData[0] = address; + i2cRxData[1] = theRegister; + + for (int i = 0; i < numBytes && Wire.available(); i++) { + i2cRxData[2 + i] = wireRead(); + } + + // send slave address, register and received bytes + Firmata.sendSysex(SYSEX_I2C_REPLY, numBytes + 2, i2cRxData); +} + +void outputPort(byte portNumber, byte portValue, byte forceSend) +{ + // pins not configured as INPUT are cleared to zeros + portValue = portValue & portConfigInputs[portNumber]; + // only send if the value is different than previously sent + if (forceSend || previousPINs[portNumber] != portValue) { + Firmata.sendDigitalPort(portNumber, portValue); + previousPINs[portNumber] = portValue; + } +} + +/* ----------------------------------------------------------------------------- + * check all the active digital inputs for change of state, then add any events + * to the Serial output queue using Serial.print() */ +void checkDigitalInputs(void) +{ + /* Using non-looping code allows constants to be given to readPort(). + * The compiler will apply substantial optimizations if the inputs + * to readPort() are compile-time constants. */ + if (TOTAL_PORTS > 0 && reportPINs[0]) outputPort(0, readPort(0, portConfigInputs[0]), false); + if (TOTAL_PORTS > 1 && reportPINs[1]) outputPort(1, readPort(1, portConfigInputs[1]), false); + if (TOTAL_PORTS > 2 && reportPINs[2]) outputPort(2, readPort(2, portConfigInputs[2]), false); + if (TOTAL_PORTS > 3 && reportPINs[3]) outputPort(3, readPort(3, portConfigInputs[3]), false); + if (TOTAL_PORTS > 4 && reportPINs[4]) outputPort(4, readPort(4, portConfigInputs[4]), false); + if (TOTAL_PORTS > 5 && reportPINs[5]) outputPort(5, readPort(5, portConfigInputs[5]), false); + if (TOTAL_PORTS > 6 && reportPINs[6]) outputPort(6, readPort(6, portConfigInputs[6]), false); + if (TOTAL_PORTS > 7 && reportPINs[7]) outputPort(7, readPort(7, portConfigInputs[7]), false); + if (TOTAL_PORTS > 8 && reportPINs[8]) outputPort(8, readPort(8, portConfigInputs[8]), false); + if (TOTAL_PORTS > 9 && reportPINs[9]) outputPort(9, readPort(9, portConfigInputs[9]), false); + if (TOTAL_PORTS > 10 && reportPINs[10]) outputPort(10, readPort(10, portConfigInputs[10]), false); + if (TOTAL_PORTS > 11 && reportPINs[11]) outputPort(11, readPort(11, portConfigInputs[11]), false); + if (TOTAL_PORTS > 12 && reportPINs[12]) outputPort(12, readPort(12, portConfigInputs[12]), false); + if (TOTAL_PORTS > 13 && reportPINs[13]) outputPort(13, readPort(13, portConfigInputs[13]), false); + if (TOTAL_PORTS > 14 && reportPINs[14]) outputPort(14, readPort(14, portConfigInputs[14]), false); + if (TOTAL_PORTS > 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false); +} + +// ----------------------------------------------------------------------------- +/* sets the pin mode to the correct state and sets the relevant bits in the + * two bit-arrays that track Digital I/O and PWM status + */ +void setPinModeCallback(byte pin, int mode) +{ + if (pinConfig[pin] == IGNORE) + return; + + if (pinConfig[pin] == I2C && isI2CEnabled && mode != I2C) { + // disable i2c so pins can be used for other functions + // the following if statements should reconfigure the pins properly + disableI2CPins(); + } + if (IS_PIN_DIGITAL(pin) && mode != SERVO) { + if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { + detachServo(pin); + } + } + if (IS_PIN_ANALOG(pin)) { + reportAnalogCallback(PIN_TO_ANALOG(pin), mode == ANALOG ? 1 : 0); // turn on/off reporting + } + if (IS_PIN_DIGITAL(pin)) { + if (mode == INPUT) { + portConfigInputs[pin / 8] |= (1 << (pin & 7)); + } else { + portConfigInputs[pin / 8] &= ~(1 << (pin & 7)); + } + } + pinState[pin] = 0; + switch (mode) { + case ANALOG: + if (IS_PIN_ANALOG(pin)) { + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups + } + pinConfig[pin] = ANALOG; + } + break; + case INPUT: + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups + pinConfig[pin] = INPUT; + } + break; + case OUTPUT: + if (IS_PIN_DIGITAL(pin)) { + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable PWM + pinMode(PIN_TO_DIGITAL(pin), OUTPUT); + pinConfig[pin] = OUTPUT; + } + break; + case PWM: + if (IS_PIN_PWM(pin)) { + pinMode(PIN_TO_PWM(pin), OUTPUT); + analogWrite(PIN_TO_PWM(pin), 0); + pinConfig[pin] = PWM; + } + break; + case SERVO: + if (IS_PIN_DIGITAL(pin)) { + pinConfig[pin] = SERVO; + if (servoPinMap[pin] == 255 || !servos[servoPinMap[pin]].attached()) { + // pass -1 for min and max pulse values to use default values set + // by Servo library + attachServo(pin, -1, -1); + } + } + break; + case I2C: + if (IS_PIN_I2C(pin)) { + // mark the pin as i2c + // the user must call I2C_CONFIG to enable I2C for a device + pinConfig[pin] = I2C; + } + break; + default: + Firmata.sendString("Unknown pin mode"); // TODO: put error msgs in EEPROM + } + // TODO: save status to EEPROM here, if changed +} + +void analogWriteCallback(byte pin, int value) +{ + if (pin < TOTAL_PINS) { + switch (pinConfig[pin]) { + case SERVO: + if (IS_PIN_DIGITAL(pin)) + servos[servoPinMap[pin]].write(value); + pinState[pin] = value; + break; + case PWM: + if (IS_PIN_PWM(pin)) + analogWrite(PIN_TO_PWM(pin), value); + pinState[pin] = value; + break; + } + } +} + +void digitalWriteCallback(byte port, int value) +{ + byte pin, lastPin, mask = 1, pinWriteMask = 0; + + if (port < TOTAL_PORTS) { + // create a mask of the pins on this port that are writable. + lastPin = port * 8 + 8; + if (lastPin > TOTAL_PINS) lastPin = TOTAL_PINS; + for (pin = port * 8; pin < lastPin; pin++) { + // do not disturb non-digital pins (eg, Rx & Tx) + if (IS_PIN_DIGITAL(pin)) { + // only write to OUTPUT and INPUT (enables pullup) + // do not touch pins in PWM, ANALOG, SERVO or other modes + if (pinConfig[pin] == OUTPUT || pinConfig[pin] == INPUT) { + pinWriteMask |= mask; + pinState[pin] = ((byte)value & mask) ? 1 : 0; + } + } + mask = mask << 1; + } + writePort(port, (byte)value, pinWriteMask); + } +} + + +// ----------------------------------------------------------------------------- +/* sets bits in a bit array (int) to toggle the reporting of the analogIns + */ +//void FirmataClass::setAnalogPinReporting(byte pin, byte state) { +//} +void reportAnalogCallback(byte analogPin, int value) +{ + if (analogPin < TOTAL_ANALOG_PINS) { + if (value == 0) { + analogInputsToReport = analogInputsToReport & ~ (1 << analogPin); + } else { + analogInputsToReport = analogInputsToReport | (1 << analogPin); + // prevent during system reset or all analog pin values will be reported + // which may report noise for unconnected analog pins + if (!isResetting) { + // Send pin value immediately. This is helpful when connected via + // ethernet, wi-fi or bluetooth so pin states can be known upon + // reconnecting. + Firmata.sendAnalog(analogPin, analogRead(analogPin)); + } + } + } + // TODO: save status to EEPROM here, if changed +} + +void reportDigitalCallback(byte port, int value) +{ + if (port < TOTAL_PORTS) { + reportPINs[port] = (byte)value; + // Send port value immediately. This is helpful when connected via + // ethernet, wi-fi or bluetooth so pin states can be known upon + // reconnecting. + if (value) outputPort(port, readPort(port, portConfigInputs[port]), true); + } + // do not disable analog reporting on these 8 pins, to allow some + // pins used for digital, others analog. Instead, allow both types + // of reporting to be enabled, but check if the pin is configured + // as analog when sampling the analog inputs. Likewise, while + // scanning digital pins, portConfigInputs will mask off values from any + // pins configured as analog +} + +/*============================================================================== + * SYSEX-BASED commands + *============================================================================*/ + +void sysexCallback(byte command, byte argc, byte *argv) +{ + byte mode; + byte slaveAddress; + byte data; + int slaveRegister; + unsigned int delayTime; + + switch (command) { + case I2C_REQUEST: + mode = argv[1] & I2C_READ_WRITE_MODE_MASK; + if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) { + Firmata.sendString("10-bit addressing not supported"); + return; + } + else { + slaveAddress = argv[0]; + } + + switch (mode) { + case I2C_WRITE: + Wire.beginTransmission(slaveAddress); + for (byte i = 2; i < argc; i += 2) { + data = argv[i] + (argv[i + 1] << 7); + wireWrite(data); + } + Wire.endTransmission(); + delayMicroseconds(70); + break; + case I2C_READ: + if (argc == 6) { + // a slave register is specified + slaveRegister = argv[2] + (argv[3] << 7); + data = argv[4] + (argv[5] << 7); // bytes to read + } + else { + // a slave register is NOT specified + slaveRegister = REGISTER_NOT_SPECIFIED; + data = argv[2] + (argv[3] << 7); // bytes to read + } + readAndReportData(slaveAddress, (int)slaveRegister, data); + break; + case I2C_READ_CONTINUOUSLY: + if ((queryIndex + 1) >= MAX_QUERIES) { + // too many queries, just ignore + Firmata.sendString("too many queries"); + break; + } + if (argc == 6) { + // a slave register is specified + slaveRegister = argv[2] + (argv[3] << 7); + data = argv[4] + (argv[5] << 7); // bytes to read + } + else { + // a slave register is NOT specified + slaveRegister = (int)REGISTER_NOT_SPECIFIED; + data = argv[2] + (argv[3] << 7); // bytes to read + } + queryIndex++; + query[queryIndex].addr = slaveAddress; + query[queryIndex].reg = slaveRegister; + query[queryIndex].bytes = data; + break; + case I2C_STOP_READING: + byte queryIndexToSkip; + // if read continuous mode is enabled for only 1 i2c device, disable + // read continuous reporting for that device + if (queryIndex <= 0) { + queryIndex = -1; + } else { + // if read continuous mode is enabled for multiple devices, + // determine which device to stop reading and remove it's data from + // the array, shifiting other array data to fill the space + for (byte i = 0; i < queryIndex + 1; i++) { + if (query[i].addr == slaveAddress) { + queryIndexToSkip = i; + break; + } + } + + for (byte i = queryIndexToSkip; i < queryIndex + 1; i++) { + if (i < MAX_QUERIES) { + query[i].addr = query[i + 1].addr; + query[i].reg = query[i + 1].reg; + query[i].bytes = query[i + 1].bytes; + } + } + queryIndex--; + } + break; + default: + break; + } + break; + case I2C_CONFIG: + delayTime = (argv[0] + (argv[1] << 7)); + + if (delayTime > 0) { + i2cReadDelayTime = delayTime; + } + + if (!isI2CEnabled) { + enableI2CPins(); + } + + break; + case SERVO_CONFIG: + if (argc > 4) { + // these vars are here for clarity, they'll optimized away by the compiler + byte pin = argv[0]; + int minPulse = argv[1] + (argv[2] << 7); + int maxPulse = argv[3] + (argv[4] << 7); + + if (IS_PIN_DIGITAL(pin)) { + if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { + detachServo(pin); + } + attachServo(pin, minPulse, maxPulse); + setPinModeCallback(pin, SERVO); + } + } + break; + case SAMPLING_INTERVAL: + if (argc > 1) { + samplingInterval = argv[0] + (argv[1] << 7); + if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) { + samplingInterval = MINIMUM_SAMPLING_INTERVAL; + } + } else { + //Firmata.sendString("Not enough data"); + } + break; + case EXTENDED_ANALOG: + if (argc > 1) { + int val = argv[1]; + if (argc > 2) val |= (argv[2] << 7); + if (argc > 3) val |= (argv[3] << 14); + analogWriteCallback(argv[0], val); + } + break; + case CAPABILITY_QUERY: + Firmata.write(START_SYSEX); + Firmata.write(CAPABILITY_RESPONSE); + for (byte pin = 0; pin < TOTAL_PINS; pin++) { + if (IS_PIN_DIGITAL(pin)) { + Firmata.write((byte)INPUT); + Firmata.write(1); + Firmata.write((byte)OUTPUT); + Firmata.write(1); + } + if (IS_PIN_ANALOG(pin)) { + Firmata.write(ANALOG); + Firmata.write(10); // 10 = 10-bit resolution + } + if (IS_PIN_PWM(pin)) { + Firmata.write(PWM); + Firmata.write(8); // 8 = 8-bit resolution + } + if (IS_PIN_DIGITAL(pin)) { + Firmata.write(SERVO); + Firmata.write(14); + } + if (IS_PIN_I2C(pin)) { + Firmata.write(I2C); + Firmata.write(1); // TODO: could assign a number to map to SCL or SDA + } + Firmata.write(127); + } + Firmata.write(END_SYSEX); + break; + case PIN_STATE_QUERY: + if (argc > 0) { + byte pin = argv[0]; + Firmata.write(START_SYSEX); + Firmata.write(PIN_STATE_RESPONSE); + Firmata.write(pin); + if (pin < TOTAL_PINS) { + Firmata.write((byte)pinConfig[pin]); + Firmata.write((byte)pinState[pin] & 0x7F); + if (pinState[pin] & 0xFF80) Firmata.write((byte)(pinState[pin] >> 7) & 0x7F); + if (pinState[pin] & 0xC000) Firmata.write((byte)(pinState[pin] >> 14) & 0x7F); + } + Firmata.write(END_SYSEX); + } + break; + case ANALOG_MAPPING_QUERY: + Firmata.write(START_SYSEX); + Firmata.write(ANALOG_MAPPING_RESPONSE); + for (byte pin = 0; pin < TOTAL_PINS; pin++) { + Firmata.write(IS_PIN_ANALOG(pin) ? PIN_TO_ANALOG(pin) : 127); + } + Firmata.write(END_SYSEX); + break; + } +} + +void enableI2CPins() +{ + byte i; + // is there a faster way to do this? would probaby require importing + // Arduino.h to get SCL and SDA pins + for (i = 0; i < TOTAL_PINS; i++) { + if (IS_PIN_I2C(i)) { + // mark pins as i2c so they are ignore in non i2c data requests + setPinModeCallback(i, I2C); + } + } + + isI2CEnabled = true; + + Wire.begin(); +} + +/* disable the i2c pins so they can be used for other functions */ +void disableI2CPins() { + isI2CEnabled = false; + // disable read continuous mode for all devices + queryIndex = -1; +} + +/*============================================================================== + * SETUP() + *============================================================================*/ + +void systemResetCallback() +{ + isResetting = true; + + // initialize a defalt state + // TODO: option to load config from EEPROM instead of default + + if (isI2CEnabled) { + disableI2CPins(); + } + + for (byte i = 0; i < TOTAL_PORTS; i++) { + reportPINs[i] = false; // by default, reporting off + portConfigInputs[i] = 0; // until activated + previousPINs[i] = 0; + } + + for (byte i = 0; i < TOTAL_PINS; i++) { + // pins with analog capability default to analog input + // otherwise, pins default to digital output + if (IS_PIN_ANALOG(i)) { + // turns off pullup, configures everything + setPinModeCallback(i, ANALOG); + } else { + // sets the output to 0, configures portConfigInputs + setPinModeCallback(i, OUTPUT); + } + + servoPinMap[i] = 255; + } + // by default, do not report any analog inputs + analogInputsToReport = 0; + + detachedServoCount = 0; + servoCount = 0; + + /* send digital inputs to set the initial state on the host computer, + * since once in the loop(), this firmware will only send on change */ + /* + TODO: this can never execute, since no pins default to digital input + but it will be needed when/if we support EEPROM stored config + for (byte i=0; i < TOTAL_PORTS; i++) { + outputPort(i, readPort(i, portConfigInputs[i]), true); + } + */ + isResetting = false; +} + +void setup() +{ + Serial1.begin(57600); // Set the baud. + while (!Serial1) { + } + // Wait for U-boot to finish startup. Consume all bytes until we are done. + do { + while (Serial1.available() > 0) { + Serial1.read(); + } + delay(1000); + } + while (Serial1.available() > 0); + + Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION); + + Firmata.attach(ANALOG_MESSAGE, analogWriteCallback); + Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback); + Firmata.attach(REPORT_ANALOG, reportAnalogCallback); + Firmata.attach(REPORT_DIGITAL, reportDigitalCallback); + Firmata.attach(SET_PIN_MODE, setPinModeCallback); + Firmata.attach(START_SYSEX, sysexCallback); + Firmata.attach(SYSTEM_RESET, systemResetCallback); + + Firmata.begin(Serial1); + systemResetCallback(); // reset to default config +} + +/*============================================================================== + * LOOP() + *============================================================================*/ +void loop() +{ + byte pin, analogPin; + + /* DIGITALREAD - as fast as possible, check for changes and output them to the + * FTDI buffer using Serial.print() */ + checkDigitalInputs(); + + /* STREAMREAD - processing incoming messagse as soon as possible, while still + * checking digital inputs. */ + while (Firmata.available()) + Firmata.processInput(); + + // TODO - ensure that Stream buffer doesn't go over 60 bytes + + currentMillis = millis(); + if (currentMillis - previousMillis > samplingInterval) { + previousMillis += samplingInterval; + /* ANALOGREAD - do all analogReads() at the configured sampling interval */ + for (pin = 0; pin < TOTAL_PINS; pin++) { + if (IS_PIN_ANALOG(pin) && pinConfig[pin] == ANALOG) { + analogPin = PIN_TO_ANALOG(pin); + if (analogInputsToReport & (1 << analogPin)) { + Firmata.sendAnalog(analogPin, analogRead(analogPin)); + } + } + } + // report i2c data for all device with read continuous mode enabled + if (queryIndex > -1) { + for (byte i = 0; i < queryIndex + 1; i++) { + readAndReportData(query[i].addr, query[i].reg, query[i].bytes); + } + } + } +} diff --git a/libraries/Firmata/extras/readme.md b/libraries/Firmata/extras/readme.md index 498a231b9de..d1556d690c1 100644 --- a/libraries/Firmata/extras/readme.md +++ b/libraries/Firmata/extras/readme.md @@ -1,6 +1,8 @@ #Firmata -Firmata is a protocol for communicating with microcontrollers from software on a host computer. The [protocol](http://firmata.org/wiki/Protocol) can be implemented in firmware on any microcontroller architecture as well as software on any host computer software package. The arduino repository described here is a Firmata library for Arduino and Arduino-compatible devices. See the [firmata wiki](http://firmata.org/wiki/Main_Page) for additional informataion. If you would like to contribute to Firmata, please see the [Contributing](#contributing) section below. +[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/firmata/arduino?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +Firmata is a protocol for communicating with microcontrollers from software on a host computer. The [protocol](https://github.com/firmata/protocol) can be implemented in firmware on any microcontroller architecture as well as software on any host computer software package. The arduino repository described here is a Firmata library for Arduino and Arduino-compatible devices. If you would like to contribute to Firmata, please see the [Contributing](#contributing) section below. ##Usage @@ -18,29 +20,42 @@ Most of the time you will be interacting with arduino with a client library on t * [https://github.com/firmata/pyduino] * [https://github.com/lupeke/python-firmata] * [https://github.com/tino/pyFirmata] + * [https://github.com/MrYsLab/PyMata] * perl * [https://github.com/ntruchsess/perl-firmata] * [https://github.com/rcaputo/rx-firmata] -* ruby +* ruby * [https://github.com/hardbap/firmata] * [https://github.com/PlasticLizard/rufinol] * [http://funnel.cc] * clojure * [https://github.com/nakkaya/clodiuno] -* javascript + * [https://github.com/peterschwarz/clj-firmata] +* javascript * [https://github.com/jgautier/firmata] * [http://breakoutjs.com] * [https://github.com/rwldrn/johnny-five] -* java +* java * [https://github.com/4ntoine/Firmata] * [https://github.com/shigeodayo/Javarduino] + * [https://github.com/kurbatov/firmata4j] * .NET + * [https://github.com/SolidSoils/Arduino] * [http://www.imagitronics.org/projects/firmatanet/] * Flash/AS3 * [http://funnel.cc] * [http://code.google.com/p/as3glue/] * PHP * [https://bitbucket.org/ThomasWeinert/carica-firmata] + * [https://github.com/oasynnoum/phpmake_firmata] +* Haskell + * [http://hackage.haskell.org/package/hArduino] +* iOS + * [https://github.com/jacobrosenthal/iosfirmata] +* Dart + * [https://github.com/nfrancois/firmata] +* Max/MSP + * [http://www.maxuino.org/] Note: The above libraries may support various versions of the Firmata protocol and therefore may not support all features of the latest Firmata spec nor all arduino and arduino-compatible boards. Refer to the respective projects for details. @@ -50,22 +65,25 @@ Note: The above libraries may support various versions of the Firmata protocol a If you discover a bug or would like to propose a new feature, please open a new [issue](https://github.com/firmata/arduino/issues?sort=created&state=open). Due to the limited memory of standard Arduino boards we cannot add every requested feature to StandardFirmata. Requests to add new features to StandardFirmata will be evaluated by the Firmata developers. However it is still possible to add new features to other Firmata implementations (Firmata is a protocol whereas StandardFirmata is just one of many possible implementations). -To contribute, fork this respository and create a new topic branch for the bug, feature or other existing issue you are addressing. Submit the pull request against the *dev* branch. +To contribute, fork this repository and create a new topic branch for the bug, feature or other existing issue you are addressing. Submit the pull request against the *master* branch. If you would like to contribute but don't have a specific bugfix or new feature to contribute, you can take on an existing issue, see issues labeled "pull-request-encouraged". Add a comment to the issue to express your intent to begin work and/or to get any additional information about the issue. -You must thorougly test your contributed code. In your pull request, describe tests performed to ensure that no existing code is broken and that any changes maintain backwards compatibility with the existing api. Test on multiple Arduino board variants if possible. We hope to enable some form of automated (or at least semi-automated) testing in the future, but for now any tests will need to be executed manually by the contributor and reviewsers. +You must thoroughly test your contributed code. In your pull request, describe tests performed to ensure that no existing code is broken and that any changes maintain backwards compatibility with the existing api. Test on multiple Arduino board variants if possible. We hope to enable some form of automated (or at least semi-automated) testing in the future, but for now any tests will need to be executed manually by the contributor and reviewers. -Maintain the existing code style: +Use [Artistic Style](http://astyle.sourceforge.net/) (astyle) to format your code. Set the following rules for the astyle formatter: -- Indentation is 2 spaces -- Use spaces instead of tabs -- Use camel case for both private and public properties and methods -- Document functions (specific doc style is TBD... for now just be sure to document) -- Insert first block bracket on line following the function definition: +``` +style = "" +indent = "spaces" +indent-spaces = 2 +indent-classes = true +indent-switches = true +indent-cases = true +indent-col1-comments = true +pad-oper = true +pad-header = true +keep-one-line-statements = true +``` -

void someFunction()
-{
-  // do something
-}
-
+If you happen to use Sublime Text, [this astyle plugin](https://github.com/timonwong/SublimeAStyleFormatter) is helpful. Set the above rules in the user settings file. diff --git a/libraries/Firmata/extras/revisions.txt b/libraries/Firmata/extras/revisions.txt new file mode 100644 index 00000000000..9a933d24781 --- /dev/null +++ b/libraries/Firmata/extras/revisions.txt @@ -0,0 +1,141 @@ +FIRMATA 2.4.3 - Apr 11, 2015 + +[core library] +* Added debug macros (utility/firmataDebug.h) +* Added Norbert Truchsess' EthernetClientStream lib from the configurable branch + +[examples] +* Added StandardFirmataEthernet to enable Firmata over Ethernet +* Minor updates to StandardFirmata and StandardFirmataYun + +FIRMATA 2.4.2 - Mar 16, 2015 + +[core library] +* Add support for Teesy 3.1 (Olivier Louvignes) + +FIRMATA 2.4.1 - Feb 7, 2015 + +[core library] +* Fixed off-by-one bug in setFirmwareNameAndVersion (Brian Schmalz) + +[StandardFirmata] +* Prevent analog values from being reported during system reset + +FIRMATA 2.4.0 - Dec 21, 2014 + +Changes from 2.3.6 to 2.4 that may impact existing Firmata client implementations: + +* When sending a string from the client application to the board (STRING_DATA) a +static buffer is now used for the incoming string in place of a dynamically allocated +block of memory (see Firmata.cpp lines 181 - 205). In Firmata 2.3.6 and older, +the dynamically allocated block was never freed, causing a memory leak. If your +client library had freed this memory in the string callback method, that code +will break in Firmata 2.4. If the string data needs to persist beyond the string +callback, it should be copied within the string callback. + +* As of Firmata 2.4, when digital port reporting or analog pin reporting is enabled, +the value of the port (digital) or pin (analog) is immediately sent back to the client +application. This will likely not have a negative impact on existing client +implementations, but may be unexpected. This feature was added to better support +non-serial streams (such as Ethernet, Wi-Fi, Bluetooth, etc) that may lose +connectivity and need a quick way to get the current state of the pins upon +reestablishing a connection. + +[core library] +* Changed sendValueAsTwo7bitBytes, startSysex and endSysex from private to + public methods. +* Added Intel Galileo to Boards.h +* Renamed FirmataSerial to FirmataStream +* Updated to latest Arduino library format +* writePort function in Boards.h now returns 1 (to suppress compiler warning) +* Updated syntax highlighting (keywords.txt) +* Fixed IS_PIN_SPI ifndef condition in boards.h +* Added constants to Firmata.h to reserve configurable firmata features +* Fixed issue where firmwareName was not reported correctly in Windows +* Ensure incoming String via STRING_DATA command is null-terminated +* Fixed memory leak when receiving String via STRING_DATA command + (note this change may break existing code if you were manually deallocating + the incoming string in your string callback function. See code for details) +* Added ability for user to specify a filename when calling setFirmwareNameAndVersion +* Increased input data buffer size from 32 to 64 bytes + +[StandardFirmata] +* Updated I2C_READ_CONTINUOUSLY to work with or without slaveRegister (Rick Waldron) +* Added Yun variant of StandardFirmata +* When a digital port is enabled, its value is now immediately sent to the client +* When an analog pin is enabled, its value is now immediately sent to the client +* Changed the way servo pins are mapped to enable use of servos on + a wider range of pins, including analog pins. +* Fixed management of unexpected sized I2C replies (Nahuel Greco) +* Fixed a bug when removing a monitored device with I2C_STOP_Reading (Nahuel Greco) +* Fixed conditional expression in I2C_STOP_READING case +* Changed samplingInterval from type int to type unsigned int +* Shortened error message strings to save a few bytes + +[examples] +* Updated FirmataServo example to use new pin mapping technique +* Removed makefiles from examples (because they were not being updated) +* Updated all examples to set current firmware version + +FIRMATA 2.3.6 - Jun 18, 2013 (Version included with Arduino core libraries) + +[core library] +* Fixed bug introduced in 2.3.5 that broke ability to use Ethernet. + +FIRMATA 2.3.5 - May 21, 2013 + +[core library] +* Added Arduino Due to Boards.h +* Added Teensy 3.0 to Boards.h +* Updated unit tests to use ArduinoUnit v2.0 +* Renamed pin13strobe to strobeBlinkPin +* Removed blinkVersion method from begin method for non-serial streams +* Fixed memory leak in setting firmware version (Matthew Murdoch) +* Added unit tests for a few core functions (Matthew Murdoch) +* Added IS_PIN_SPI macro to all board definitions in Board.h (Norbert Truchsess) + +FIRMATA 2.3.4 - Feb 11, 2013 + +[core library] +* Fixed Stream implementation so Firmata can be used with Streams other than + Serial (Norbert Truchsess) + +FIRMATA 2.3.3 - Oct 6, 2012 + +[core library] +* Added write method to expose FirmataSerial.write +* Added Arduino Leonardo to Boards.h + +[StandardFirmata] +* Changed all instances of Serial.write to Firmata.write +* Fixed delayMicroseconds(0) bug in readAndReportData + +FIRMATA 2.3.0 - 2.3.2 + +* Removed angle from servo config +* Changed file extensions from .pde to .ino +* Added MEGA2560 to Boards.h +* Added I2C pins to Boards.h +* Modified examples to be compatible with Arduino 0022 and 1.0 or greater +* Removed I2CFirmata example +* Changes to StandardFirmata + * Added I2C support + * Added system reset message to reset all pins to default config on sysex reset + +FIRMATA 2.2 (changes prior to Firmata 2.3.0 were not well documented) + +* changes undocumented + +FIRMATA 2.1 + +* added support for changing the sampling interval +* added Servo support + +FIRMATA 2.0 + +* changed to 8-bit port-based digital messages to mirror ports from previous 14-bit ports modeled after the standard Arduino board. +* switched order of version message so major version is reported first + +FIRMATA 1.0 + +* switched to MIDI-compatible packet format (though the message interpretation differs) diff --git a/libraries/Firmata/keywords.txt b/libraries/Firmata/keywords.txt index ca4522c3d40..f384be18256 100644 --- a/libraries/Firmata/keywords.txt +++ b/libraries/Firmata/keywords.txt @@ -6,57 +6,66 @@ # Datatypes (KEYWORD1) ####################################### -Firmata KEYWORD1 Firmata -callbackFunction KEYWORD1 -systemResetCallbackFunction KEYWORD1 -stringCallbackFunction KEYWORD1 -sysexCallbackFunction KEYWORD1 +Firmata KEYWORD1 +callbackFunction KEYWORD1 +systemResetCallbackFunction KEYWORD1 +stringCallbackFunction KEYWORD1 +sysexCallbackFunction KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### -begin KEYWORD2 -begin KEYWORD2 -printVersion KEYWORD2 -blinkVersion KEYWORD2 -printFirmwareVersion KEYWORD2 -setFirmwareVersion KEYWORD2 -setFirmwareNameAndVersion KEYWORD2 -available KEYWORD2 -processInput KEYWORD2 -sendAnalog KEYWORD2 -sendDigital KEYWORD2 -sendDigitalPortPair KEYWORD2 -sendDigitalPort KEYWORD2 -sendString KEYWORD2 -sendString KEYWORD2 -sendSysex KEYWORD2 -attach KEYWORD2 -detach KEYWORD2 -flush KEYWORD2 +begin KEYWORD2 +printVersion KEYWORD2 +blinkVersion KEYWORD2 +printFirmwareVersion KEYWORD2 +setFirmwareVersion KEYWORD2 +setFirmwareNameAndVersion KEYWORD2 +available KEYWORD2 +processInput KEYWORD2 +sendAnalog KEYWORD2 +sendDigital KEYWORD2 +sendDigitalPort KEYWORD2 +sendString KEYWORD2 +sendSysex KEYWORD2 +attach KEYWORD2 +detach KEYWORD2 +write KEYWORD2 +sendValueAsTwo7bitBytes KEYWORD2 +startSysex KEYWORD2 +endSysex KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### -MAX_DATA_BYTES LITERAL1 +MAX_DATA_BYTES LITERAL1 -DIGITAL_MESSAGE LITERAL1 -ANALOG_MESSAGE LITERAL1 -REPORT_ANALOG LITERAL1 -REPORT_DIGITAL LITERAL1 -REPORT_VERSION LITERAL1 -SET_PIN_MODE LITERAL1 -SYSTEM_RESET LITERAL1 +DIGITAL_MESSAGE LITERAL1 +ANALOG_MESSAGE LITERAL1 +REPORT_ANALOG LITERAL1 +REPORT_DIGITAL LITERAL1 +REPORT_VERSION LITERAL1 +SET_PIN_MODE LITERAL1 +SYSTEM_RESET LITERAL1 +START_SYSEX LITERAL1 +END_SYSEX LITERAL1 -START_SYSEX LITERAL1 -END_SYSEX LITERAL1 +ANALOG LITERAL1 +PWM LITERAL1 +SERVO LITERAL1 +SHIFT LITERAL1 +I2C LITERAL1 +ONEWIRE LITERAL1 +STEPPER LITERAL1 +ENCODER LITERAL1 +IGNORE LITERAL1 -PWM LITERAL1 -TOTAL_ANALOG_PINS LITERAL1 -TOTAL_DIGITAL_PINS LITERAL1 -TOTAL_PORTS LITERAL1 -ANALOG_PORT LITERAL1 +TOTAL_ANALOG_PINS LITERAL1 +TOTAL_DIGITAL_PINS LITERAL1 +TOTAL_PIN_MODES LITERAL1 +TOTAL_PORTS LITERAL1 +ANALOG_PORT LITERAL1 diff --git a/libraries/Firmata/library.properties b/libraries/Firmata/library.properties index 4b508d57cc6..fd9eea05d9b 100644 --- a/libraries/Firmata/library.properties +++ b/libraries/Firmata/library.properties @@ -1,9 +1,9 @@ -name=Firmata -version=2.3.8 -author=Firmata Developers -maintainer=Firmata Developers -sentence=Enables the communication with computer apps using a standard serial protocol. For all Arduino boards. -paragraph=The Firmata library implements the Firmata protocol for communicating with software on the host computer. This allows you to write custom firmware without having to create your own protocol and objects for the programming environment that you are using. -category=Device Control -url=http://firmata.org -architectures=* +name=Firmata +version=2.4.3 +author=Firmata Developers +maintainer=https://github.com/firmata/arduino +sentence=Enables the communication with computer apps using a standard serial protocol. For all Arduino boards. +paragraph=The Firmata library implements the Firmata protocol for communicating with software on the host computer. This allows you to write custom firmware without having to create your own protocol and objects for the programming environment that you are using. +category=Device Control +url=https://github.com/firmata/arduino +architectures=* diff --git a/libraries/Firmata/src/Boards.h b/libraries/Firmata/src/Boards.h index 67cae11a8fb..37a3da19a34 100644 --- a/libraries/Firmata/src/Boards.h +++ b/libraries/Firmata/src/Boards.h @@ -1,4 +1,14 @@ -/* Boards.h - Hardware Abstraction Layer for Firmata library */ +/* + Boards.h - Hardware Abstraction Layer for Firmata library + Copyright (c) 2006-2008 Hans-Christoph Steiner. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + See file LICENSE.txt for further informations on licensing terms. +*/ #ifndef Firmata_Boards_h #define Firmata_Boards_h @@ -6,7 +16,7 @@ #include #if defined(ARDUINO) && ARDUINO >= 100 -#include "Arduino.h" // for digitalRead, digitalWrite, etc +#include "Arduino.h" // for digitalRead, digitalWrite, etc #else #include "WProgram.h" #endif @@ -23,7 +33,7 @@ Firmata Hardware Abstraction Layer Firmata is built on top of the hardware abstraction functions of Arduino, -specifically digitalWrite, digitalRead, analogWrite, analogRead, and +specifically digitalWrite, digitalRead, analogWrite, analogRead, and pinMode. While these functions offer simple integer pin numbers, Firmata needs more information than is provided by Arduino. This file provides all other hardware specific details. To make Firmata support a new board, @@ -163,7 +173,7 @@ writePort(port, value, bitmask): Write an 8 bit port. #define PIN_TO_DIGITAL(p) (p) #define PIN_TO_ANALOG(p) ((p) - FIRST_ANALOG_PIN) #define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) -#define PIN_TO_SERVO(p) (p) +#define PIN_TO_SERVO(p) (p) // old Arduinos @@ -250,8 +260,8 @@ writePort(port, value, bitmask): Write an 8 bit port. #define PIN_TO_SERVO(p) (p) -// Teensy 3.0 -#elif defined(__MK20DX128__) +// Teensy 3.0 and 3.1 +#elif defined(__MK20DX128__) || defined(__MK20DX256__) #define TOTAL_ANALOG_PINS 14 #define TOTAL_PINS 38 // 24 digital + 10 analog-digital + 4 analog #define VERSION_BLINK_PIN 13 @@ -263,7 +273,7 @@ writePort(port, value, bitmask): Write an 8 bit port. #define PIN_TO_DIGITAL(p) (p) #define PIN_TO_ANALOG(p) (((p)<=23)?(p)-14:(p)-24) #define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) -#define PIN_TO_SERVO(p) (p) +#define PIN_TO_SERVO(p) (p) // Teensy++ 1.0 and 2.0 @@ -297,7 +307,24 @@ writePort(port, value, bitmask): Write an 8 bit port. #define PIN_TO_DIGITAL(p) (p) #define PIN_TO_ANALOG(p) (p) - 18 #define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) -#define PIN_TO_SERVO(p) (p) +#define PIN_TO_SERVO(p) (p) + + +// Intel Galileo Board +#elif defined(ARDUINO_LINUX) +#define TOTAL_ANALOG_PINS 6 +#define TOTAL_PINS 20 // 14 digital + 6 analog +#define VERSION_BLINK_PIN 13 +#define IS_PIN_DIGITAL(p) ((p) >= 2 && (p) <= 19) +#define IS_PIN_ANALOG(p) ((p) >= 14 && (p) <= 19) +#define IS_PIN_PWM(p) digitalPinHasPWM(p) +#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) - 2 < MAX_SERVOS) +#define IS_PIN_I2C(p) ((p) == SDA || (p) == SCL) +#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK) +#define PIN_TO_DIGITAL(p) (p) +#define PIN_TO_ANALOG(p) ((p) - 14) +#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) +#define PIN_TO_SERVO(p) ((p) - 2) // Sanguino @@ -338,7 +365,7 @@ writePort(port, value, bitmask): Write an 8 bit port. #endif // as long this is not defined for all boards: -#ifndef IS_PIN_SPI(p) +#ifndef IS_PIN_SPI #define IS_PIN_SPI(p) 0 #endif @@ -350,21 +377,21 @@ static inline unsigned char readPort(byte, byte) __attribute__((always_inline, u static inline unsigned char readPort(byte port, byte bitmask) { #if defined(ARDUINO_PINOUT_OPTIMIZE) - if (port == 0) return (PIND & 0xFC) & bitmask; // ignore Rx/Tx 0/1 - if (port == 1) return ((PINB & 0x3F) | ((PINC & 0x03) << 6)) & bitmask; - if (port == 2) return ((PINC & 0x3C) >> 2) & bitmask; - return 0; + if (port == 0) return (PIND & 0xFC) & bitmask; // ignore Rx/Tx 0/1 + if (port == 1) return ((PINB & 0x3F) | ((PINC & 0x03) << 6)) & bitmask; + if (port == 2) return ((PINC & 0x3C) >> 2) & bitmask; + return 0; #else - unsigned char out=0, pin=port*8; - if (IS_PIN_DIGITAL(pin+0) && (bitmask & 0x01) && digitalRead(PIN_TO_DIGITAL(pin+0))) out |= 0x01; - if (IS_PIN_DIGITAL(pin+1) && (bitmask & 0x02) && digitalRead(PIN_TO_DIGITAL(pin+1))) out |= 0x02; - if (IS_PIN_DIGITAL(pin+2) && (bitmask & 0x04) && digitalRead(PIN_TO_DIGITAL(pin+2))) out |= 0x04; - if (IS_PIN_DIGITAL(pin+3) && (bitmask & 0x08) && digitalRead(PIN_TO_DIGITAL(pin+3))) out |= 0x08; - if (IS_PIN_DIGITAL(pin+4) && (bitmask & 0x10) && digitalRead(PIN_TO_DIGITAL(pin+4))) out |= 0x10; - if (IS_PIN_DIGITAL(pin+5) && (bitmask & 0x20) && digitalRead(PIN_TO_DIGITAL(pin+5))) out |= 0x20; - if (IS_PIN_DIGITAL(pin+6) && (bitmask & 0x40) && digitalRead(PIN_TO_DIGITAL(pin+6))) out |= 0x40; - if (IS_PIN_DIGITAL(pin+7) && (bitmask & 0x80) && digitalRead(PIN_TO_DIGITAL(pin+7))) out |= 0x80; - return out; + unsigned char out = 0, pin = port * 8; + if (IS_PIN_DIGITAL(pin + 0) && (bitmask & 0x01) && digitalRead(PIN_TO_DIGITAL(pin + 0))) out |= 0x01; + if (IS_PIN_DIGITAL(pin + 1) && (bitmask & 0x02) && digitalRead(PIN_TO_DIGITAL(pin + 1))) out |= 0x02; + if (IS_PIN_DIGITAL(pin + 2) && (bitmask & 0x04) && digitalRead(PIN_TO_DIGITAL(pin + 2))) out |= 0x04; + if (IS_PIN_DIGITAL(pin + 3) && (bitmask & 0x08) && digitalRead(PIN_TO_DIGITAL(pin + 3))) out |= 0x08; + if (IS_PIN_DIGITAL(pin + 4) && (bitmask & 0x10) && digitalRead(PIN_TO_DIGITAL(pin + 4))) out |= 0x10; + if (IS_PIN_DIGITAL(pin + 5) && (bitmask & 0x20) && digitalRead(PIN_TO_DIGITAL(pin + 5))) out |= 0x20; + if (IS_PIN_DIGITAL(pin + 6) && (bitmask & 0x40) && digitalRead(PIN_TO_DIGITAL(pin + 6))) out |= 0x40; + if (IS_PIN_DIGITAL(pin + 7) && (bitmask & 0x80) && digitalRead(PIN_TO_DIGITAL(pin + 7))) out |= 0x80; + return out; #endif } @@ -376,40 +403,42 @@ static inline unsigned char writePort(byte, byte, byte) __attribute__((always_in static inline unsigned char writePort(byte port, byte value, byte bitmask) { #if defined(ARDUINO_PINOUT_OPTIMIZE) - if (port == 0) { - bitmask = bitmask & 0xFC; // do not touch Tx & Rx pins - byte valD = value & bitmask; - byte maskD = ~bitmask; - cli(); - PORTD = (PORTD & maskD) | valD; - sei(); - } else if (port == 1) { - byte valB = (value & bitmask) & 0x3F; - byte valC = (value & bitmask) >> 6; - byte maskB = ~(bitmask & 0x3F); - byte maskC = ~((bitmask & 0xC0) >> 6); - cli(); - PORTB = (PORTB & maskB) | valB; - PORTC = (PORTC & maskC) | valC; - sei(); - } else if (port == 2) { - bitmask = bitmask & 0x0F; - byte valC = (value & bitmask) << 2; - byte maskC = ~(bitmask << 2); - cli(); - PORTC = (PORTC & maskC) | valC; - sei(); - } + if (port == 0) { + bitmask = bitmask & 0xFC; // do not touch Tx & Rx pins + byte valD = value & bitmask; + byte maskD = ~bitmask; + cli(); + PORTD = (PORTD & maskD) | valD; + sei(); + } else if (port == 1) { + byte valB = (value & bitmask) & 0x3F; + byte valC = (value & bitmask) >> 6; + byte maskB = ~(bitmask & 0x3F); + byte maskC = ~((bitmask & 0xC0) >> 6); + cli(); + PORTB = (PORTB & maskB) | valB; + PORTC = (PORTC & maskC) | valC; + sei(); + } else if (port == 2) { + bitmask = bitmask & 0x0F; + byte valC = (value & bitmask) << 2; + byte maskC = ~(bitmask << 2); + cli(); + PORTC = (PORTC & maskC) | valC; + sei(); + } + return 1; #else - byte pin=port*8; - if ((bitmask & 0x01)) digitalWrite(PIN_TO_DIGITAL(pin+0), (value & 0x01)); - if ((bitmask & 0x02)) digitalWrite(PIN_TO_DIGITAL(pin+1), (value & 0x02)); - if ((bitmask & 0x04)) digitalWrite(PIN_TO_DIGITAL(pin+2), (value & 0x04)); - if ((bitmask & 0x08)) digitalWrite(PIN_TO_DIGITAL(pin+3), (value & 0x08)); - if ((bitmask & 0x10)) digitalWrite(PIN_TO_DIGITAL(pin+4), (value & 0x10)); - if ((bitmask & 0x20)) digitalWrite(PIN_TO_DIGITAL(pin+5), (value & 0x20)); - if ((bitmask & 0x40)) digitalWrite(PIN_TO_DIGITAL(pin+6), (value & 0x40)); - if ((bitmask & 0x80)) digitalWrite(PIN_TO_DIGITAL(pin+7), (value & 0x80)); + byte pin = port * 8; + if ((bitmask & 0x01)) digitalWrite(PIN_TO_DIGITAL(pin + 0), (value & 0x01)); + if ((bitmask & 0x02)) digitalWrite(PIN_TO_DIGITAL(pin + 1), (value & 0x02)); + if ((bitmask & 0x04)) digitalWrite(PIN_TO_DIGITAL(pin + 2), (value & 0x04)); + if ((bitmask & 0x08)) digitalWrite(PIN_TO_DIGITAL(pin + 3), (value & 0x08)); + if ((bitmask & 0x10)) digitalWrite(PIN_TO_DIGITAL(pin + 4), (value & 0x10)); + if ((bitmask & 0x20)) digitalWrite(PIN_TO_DIGITAL(pin + 5), (value & 0x20)); + if ((bitmask & 0x40)) digitalWrite(PIN_TO_DIGITAL(pin + 6), (value & 0x40)); + if ((bitmask & 0x80)) digitalWrite(PIN_TO_DIGITAL(pin + 7), (value & 0x80)); + return 1; #endif } diff --git a/libraries/Firmata/src/Firmata.cpp b/libraries/Firmata/src/Firmata.cpp index 7dddfd46566..7ecead8c1a3 100644 --- a/libraries/Firmata/src/Firmata.cpp +++ b/libraries/Firmata/src/Firmata.cpp @@ -1,7 +1,7 @@ /* - Firmata.cpp - Firmata library - Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved. - + Firmata.cpp - Firmata library v2.4.3 - 2015-4-11 + Copyright (c) 2006-2008 Hans-Christoph Steiner. All rights reserved. + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -28,18 +28,18 @@ extern "C" { void FirmataClass::sendValueAsTwo7bitBytes(int value) { - FirmataSerial->write(value & B01111111); // LSB - FirmataSerial->write(value >> 7 & B01111111); // MSB + FirmataStream->write(value & B01111111); // LSB + FirmataStream->write(value >> 7 & B01111111); // MSB } void FirmataClass::startSysex(void) { - FirmataSerial->write(START_SYSEX); + FirmataStream->write(START_SYSEX); } void FirmataClass::endSysex(void) { - FirmataSerial->write(END_SYSEX); + FirmataStream->write(END_SYSEX); } //****************************************************************************** @@ -67,7 +67,7 @@ void FirmataClass::begin(void) void FirmataClass::begin(long speed) { Serial.begin(speed); - FirmataSerial = &Serial; + FirmataStream = &Serial; blinkVersion(); printVersion(); printFirmwareVersion(); @@ -76,7 +76,7 @@ void FirmataClass::begin(long speed) /* begin method for overriding default stream */ void FirmataClass::begin(Stream &s) { - FirmataSerial = &s; + FirmataStream = &s; // do not call blinkVersion() here because some hardware such as the // Ethernet shield use pin 13 printVersion(); @@ -84,16 +84,17 @@ void FirmataClass::begin(Stream &s) } // output the protocol version message to the serial port -void FirmataClass::printVersion(void) { - FirmataSerial->write(REPORT_VERSION); - FirmataSerial->write(FIRMATA_MAJOR_VERSION); - FirmataSerial->write(FIRMATA_MINOR_VERSION); +void FirmataClass::printVersion(void) +{ + FirmataStream->write(REPORT_VERSION); + FirmataStream->write(FIRMATA_MAJOR_VERSION); + FirmataStream->write(FIRMATA_MINOR_VERSION); } void FirmataClass::blinkVersion(void) { // flash the pin with the protocol version - pinMode(VERSION_BLINK_PIN,OUTPUT); + pinMode(VERSION_BLINK_PIN, OUTPUT); strobeBlinkPin(FIRMATA_MAJOR_VERSION, 40, 210); delay(250); strobeBlinkPin(FIRMATA_MINOR_VERSION, 40, 210); @@ -104,12 +105,12 @@ void FirmataClass::printFirmwareVersion(void) { byte i; - if(firmwareVersionCount) { // make sure that the name has been set before reporting + if (firmwareVersionCount) { // make sure that the name has been set before reporting startSysex(); - FirmataSerial->write(REPORT_FIRMWARE); - FirmataSerial->write(firmwareVersionVector[0]); // major version number - FirmataSerial->write(firmwareVersionVector[1]); // minor version number - for(i=2; iwrite(REPORT_FIRMWARE); + FirmataStream->write(firmwareVersionVector[0]); // major version number + FirmataStream->write(firmwareVersionVector[1]); // minor version number + for (i = 2; i < firmwareVersionCount; ++i) { sendValueAsTwo7bitBytes(firmwareVersionVector[i]); } endSysex(); @@ -118,87 +119,94 @@ void FirmataClass::printFirmwareVersion(void) void FirmataClass::setFirmwareNameAndVersion(const char *name, byte major, byte minor) { - const char *filename; - char *extension; + const char *firmwareName; + const char *extension; // parse out ".cpp" and "applet/" that comes from using __FILE__ extension = strstr(name, ".cpp"); - filename = strrchr(name, '/') + 1; //points to slash, +1 gets to start of filename - // add two bytes for version numbers - if(extension && filename) { - firmwareVersionCount = extension - filename + 2; + firmwareName = strrchr(name, '/'); + + if (!firmwareName) { + // windows + firmwareName = strrchr(name, '\\'); + } + if (!firmwareName) { + // user passed firmware name + firmwareName = name; + } else { + firmwareName ++; + } + + if (!extension) { + firmwareVersionCount = strlen(firmwareName) + 2; } else { - firmwareVersionCount = strlen(name) + 2; - filename = name; + firmwareVersionCount = extension - firmwareName + 2; } + // in case anyone calls setFirmwareNameAndVersion more than once free(firmwareVersionVector); - firmwareVersionVector = (byte *) malloc(firmwareVersionCount); + firmwareVersionVector = (byte *) malloc(firmwareVersionCount + 1); firmwareVersionVector[firmwareVersionCount] = 0; firmwareVersionVector[0] = major; firmwareVersionVector[1] = minor; - strncpy((char*)firmwareVersionVector + 2, filename, firmwareVersionCount - 2); - // alas, no snprintf on Arduino - // snprintf(firmwareVersionVector, MAX_DATA_BYTES, "%c%c%s", - // (char)major, (char)minor, firmwareVersionVector); + strncpy((char *)firmwareVersionVector + 2, firmwareName, firmwareVersionCount - 2); } -// this method is only used for unit testing -// void FirmataClass::unsetFirmwareVersion() -// { -// firmwareVersionCount = 0; -// free(firmwareVersionVector); -// firmwareVersionVector = 0; -// } - //------------------------------------------------------------------------------ // Serial Receive Handling int FirmataClass::available(void) { - return FirmataSerial->available(); + return FirmataStream->available(); } void FirmataClass::processSysexMessage(void) { - switch(storedInputData[0]) { //first byte in buffer is command - case REPORT_FIRMWARE: - printFirmwareVersion(); - break; - case STRING_DATA: - if(currentStringCallback) { - byte bufferLength = (sysexBytesRead - 1) / 2; - char *buffer = (char*)malloc(bufferLength * sizeof(char)); - byte i = 1; - byte j = 0; - while(j < bufferLength) { - buffer[j] = (char)storedInputData[i]; - i++; - buffer[j] += (char)(storedInputData[i] << 7); - i++; - j++; + switch (storedInputData[0]) { //first byte in buffer is command + case REPORT_FIRMWARE: + printFirmwareVersion(); + break; + case STRING_DATA: + if (currentStringCallback) { + byte bufferLength = (sysexBytesRead - 1) / 2; + byte i = 1; + byte j = 0; + while (j < bufferLength) { + // The string length will only be at most half the size of the + // stored input buffer so we can decode the string within the buffer. + storedInputData[j] = storedInputData[i]; + i++; + storedInputData[j] += (storedInputData[i] << 7); + i++; + j++; + } + // Make sure string is null terminated. This may be the case for data + // coming from client libraries in languages that don't null terminate + // strings. + if (storedInputData[j - 1] != '\0') { + storedInputData[j] = '\0'; + } + (*currentStringCallback)((char *)&storedInputData[0]); } - (*currentStringCallback)(buffer); - } - break; - default: - if(currentSysexCallback) - (*currentSysexCallback)(storedInputData[0], sysexBytesRead - 1, storedInputData + 1); + break; + default: + if (currentSysexCallback) + (*currentSysexCallback)(storedInputData[0], sysexBytesRead - 1, storedInputData + 1); } } void FirmataClass::processInput(void) { - int inputData = FirmataSerial->read(); // this is 'int' to handle -1 when no data + int inputData = FirmataStream->read(); // this is 'int' to handle -1 when no data int command; - + // TODO make sure it handles -1 properly - + if (parsingSysex) { - if(inputData == END_SYSEX) { - //stop sysex byte + if (inputData == END_SYSEX) { + //stop sysex byte parsingSysex = false; //fire off handler function processSysexMessage(); @@ -207,43 +215,43 @@ void FirmataClass::processInput(void) storedInputData[sysexBytesRead] = inputData; sysexBytesRead++; } - } else if( (waitForData > 0) && (inputData < 128) ) { + } else if ( (waitForData > 0) && (inputData < 128) ) { waitForData--; storedInputData[waitForData] = inputData; - if( (waitForData==0) && executeMultiByteCommand ) { // got the whole message - switch(executeMultiByteCommand) { - case ANALOG_MESSAGE: - if(currentAnalogCallback) { - (*currentAnalogCallback)(multiByteChannel, - (storedInputData[0] << 7) - + storedInputData[1]); - } - break; - case DIGITAL_MESSAGE: - if(currentDigitalCallback) { - (*currentDigitalCallback)(multiByteChannel, - (storedInputData[0] << 7) - + storedInputData[1]); - } - break; - case SET_PIN_MODE: - if(currentPinModeCallback) - (*currentPinModeCallback)(storedInputData[1], storedInputData[0]); - break; - case REPORT_ANALOG: - if(currentReportAnalogCallback) - (*currentReportAnalogCallback)(multiByteChannel,storedInputData[0]); - break; - case REPORT_DIGITAL: - if(currentReportDigitalCallback) - (*currentReportDigitalCallback)(multiByteChannel,storedInputData[0]); - break; + if ( (waitForData == 0) && executeMultiByteCommand ) { // got the whole message + switch (executeMultiByteCommand) { + case ANALOG_MESSAGE: + if (currentAnalogCallback) { + (*currentAnalogCallback)(multiByteChannel, + (storedInputData[0] << 7) + + storedInputData[1]); + } + break; + case DIGITAL_MESSAGE: + if (currentDigitalCallback) { + (*currentDigitalCallback)(multiByteChannel, + (storedInputData[0] << 7) + + storedInputData[1]); + } + break; + case SET_PIN_MODE: + if (currentPinModeCallback) + (*currentPinModeCallback)(storedInputData[1], storedInputData[0]); + break; + case REPORT_ANALOG: + if (currentReportAnalogCallback) + (*currentReportAnalogCallback)(multiByteChannel, storedInputData[0]); + break; + case REPORT_DIGITAL: + if (currentReportDigitalCallback) + (*currentReportDigitalCallback)(multiByteChannel, storedInputData[0]); + break; } executeMultiByteCommand = 0; - } + } } else { // remove channel info from command byte if less than 0xF0 - if(inputData < 0xF0) { + if (inputData < 0xF0) { command = inputData & 0xF0; multiByteChannel = inputData & 0x0F; } else { @@ -251,27 +259,27 @@ void FirmataClass::processInput(void) // commands in the 0xF* range don't use channel data } switch (command) { - case ANALOG_MESSAGE: - case DIGITAL_MESSAGE: - case SET_PIN_MODE: - waitForData = 2; // two data bytes needed - executeMultiByteCommand = command; - break; - case REPORT_ANALOG: - case REPORT_DIGITAL: - waitForData = 1; // two data bytes needed - executeMultiByteCommand = command; - break; - case START_SYSEX: - parsingSysex = true; - sysexBytesRead = 0; - break; - case SYSTEM_RESET: - systemReset(); - break; - case REPORT_VERSION: - Firmata.printVersion(); - break; + case ANALOG_MESSAGE: + case DIGITAL_MESSAGE: + case SET_PIN_MODE: + waitForData = 2; // two data bytes needed + executeMultiByteCommand = command; + break; + case REPORT_ANALOG: + case REPORT_DIGITAL: + waitForData = 1; // one data byte needed + executeMultiByteCommand = command; + break; + case START_SYSEX: + parsingSysex = true; + sysexBytesRead = 0; + break; + case SYSTEM_RESET: + systemReset(); + break; + case REPORT_VERSION: + Firmata.printVersion(); + break; } } } @@ -280,15 +288,15 @@ void FirmataClass::processInput(void) // Serial Send Handling // send an analog message -void FirmataClass::sendAnalog(byte pin, int value) +void FirmataClass::sendAnalog(byte pin, int value) { // pin can only be 0-15, so chop higher bits - FirmataSerial->write(ANALOG_MESSAGE | (pin & 0xF)); + FirmataStream->write(ANALOG_MESSAGE | (pin & 0xF)); sendValueAsTwo7bitBytes(value); } // send a single digital pin in a digital message -void FirmataClass::sendDigital(byte pin, int value) +void FirmataClass::sendDigital(byte pin, int value) { /* TODO add single pin digital messages to the protocol, this needs to * track the last digital data sent so that it can be sure to change just @@ -314,31 +322,31 @@ void FirmataClass::sendDigital(byte pin, int value) // send an 8-bit port in a single digital message (protocol v2) void FirmataClass::sendDigitalPort(byte portNumber, int portData) { - FirmataSerial->write(DIGITAL_MESSAGE | (portNumber & 0xF)); - FirmataSerial->write((byte)portData % 128); // Tx bits 0-6 - FirmataSerial->write(portData >> 7); // Tx bits 7-13 + FirmataStream->write(DIGITAL_MESSAGE | (portNumber & 0xF)); + FirmataStream->write((byte)portData % 128); // Tx bits 0-6 + FirmataStream->write(portData >> 7); // Tx bits 7-13 } -void FirmataClass::sendSysex(byte command, byte bytec, byte* bytev) +void FirmataClass::sendSysex(byte command, byte bytec, byte *bytev) { byte i; startSysex(); - FirmataSerial->write(command); - for(i=0; iwrite(command); + for (i = 0; i < bytec; i++) { + sendValueAsTwo7bitBytes(bytev[i]); } endSysex(); } -void FirmataClass::sendString(byte command, const char* string) +void FirmataClass::sendString(byte command, const char *string) { sendSysex(command, strlen(string), (byte *)string); } // send a string as the protocol string type -void FirmataClass::sendString(const char* string) +void FirmataClass::sendString(const char *string) { sendString(STRING_DATA, string); } @@ -346,7 +354,7 @@ void FirmataClass::sendString(const char* string) // expose the write method void FirmataClass::write(byte c) { - FirmataSerial->write(c); + FirmataStream->write(c); } @@ -355,26 +363,26 @@ void FirmataClass::write(byte c) // generic callbacks void FirmataClass::attach(byte command, callbackFunction newFunction) { - switch(command) { - case ANALOG_MESSAGE: currentAnalogCallback = newFunction; break; - case DIGITAL_MESSAGE: currentDigitalCallback = newFunction; break; - case REPORT_ANALOG: currentReportAnalogCallback = newFunction; break; - case REPORT_DIGITAL: currentReportDigitalCallback = newFunction; break; - case SET_PIN_MODE: currentPinModeCallback = newFunction; break; + switch (command) { + case ANALOG_MESSAGE: currentAnalogCallback = newFunction; break; + case DIGITAL_MESSAGE: currentDigitalCallback = newFunction; break; + case REPORT_ANALOG: currentReportAnalogCallback = newFunction; break; + case REPORT_DIGITAL: currentReportDigitalCallback = newFunction; break; + case SET_PIN_MODE: currentPinModeCallback = newFunction; break; } } void FirmataClass::attach(byte command, systemResetCallbackFunction newFunction) { - switch(command) { - case SYSTEM_RESET: currentSystemResetCallback = newFunction; break; + switch (command) { + case SYSTEM_RESET: currentSystemResetCallback = newFunction; break; } } void FirmataClass::attach(byte command, stringCallbackFunction newFunction) { - switch(command) { - case STRING_DATA: currentStringCallback = newFunction; break; + switch (command) { + case STRING_DATA: currentStringCallback = newFunction; break; } } @@ -385,12 +393,12 @@ void FirmataClass::attach(byte command, sysexCallbackFunction newFunction) void FirmataClass::detach(byte command) { - switch(command) { - case SYSTEM_RESET: currentSystemResetCallback = NULL; break; - case STRING_DATA: currentStringCallback = NULL; break; - case START_SYSEX: currentSysexCallback = NULL; break; - default: - attach(command, (callbackFunction)NULL); + switch (command) { + case SYSTEM_RESET: currentSystemResetCallback = NULL; break; + case STRING_DATA: currentStringCallback = NULL; break; + case START_SYSEX: currentSysexCallback = NULL; break; + default: + attach(command, (callbackFunction)NULL); } } @@ -427,28 +435,26 @@ void FirmataClass::systemReset(void) executeMultiByteCommand = 0; // execute this after getting multi-byte data multiByteChannel = 0; // channel data for multiByteCommands - for(i=0; i + +//#define SERIAL_DEBUG +#include "firmataDebug.h" + +#define MILLIS_RECONNECT 5000 + +EthernetClientStream::EthernetClientStream(Client &client, IPAddress localip, IPAddress ip, const char* host, uint16_t port) +: ip(ip), + host(host), + port(port), + connected(false), + client(client), + localip(localip) +{ +} + +int +EthernetClientStream::available() +{ + return maintain() ? client.available() : 0; +} + +int +EthernetClientStream::read() +{ + return maintain() ? client.read() : -1; +} + +int +EthernetClientStream::peek() +{ + return maintain() ? client.peek() : -1; +} + +void EthernetClientStream::flush() +{ + if (maintain()) + client.flush(); +} + +size_t +EthernetClientStream::write(uint8_t c) +{ + return maintain() ? client.write(c) : 0; +} + +void +EthernetClientStream::maintain(IPAddress localip) +{ + if (this->localip!=localip) + { + this->localip = localip; + if (connected) + stop(); + } +} + +void +EthernetClientStream::stop() +{ + client.stop(); + connected = false; + time_connect = millis(); +} + +bool +EthernetClientStream::maintain() +{ + if (client && client.connected()) + return true; + + if (connected) + { + stop(); + } + else if (millis()-time_connect >= MILLIS_RECONNECT) + { + connected = host ? client.connect(host,port) : client.connect(ip,port); + if (!connected) { + time_connect = millis(); + DEBUG_PRINTLN("connection failed"); + } else { + DEBUG_PRINTLN("connected"); + } + } + return connected; +} diff --git a/libraries/Firmata/src/utility/EthernetClientStream.h b/libraries/Firmata/src/utility/EthernetClientStream.h new file mode 100644 index 00000000000..c0d34e54e3b --- /dev/null +++ b/libraries/Firmata/src/utility/EthernetClientStream.h @@ -0,0 +1,52 @@ +/* + EthernetClientStream.h + An Arduino-Stream that wraps an instance of Client reconnecting to + the remote-ip in a transparent way. A disconnected client may be + recognized by the returnvalues -1 from calls to peek or read and + a 0 from calls to write. + + Copyright (C) 2013 Norbert Truchsess. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + See file LICENSE.txt for further informations on licensing terms. + + formatted using the GNU C formatting and indenting + */ + +#ifndef ETHERNETCLIENTSTREAM_H +#define ETHERNETCLIENTSTREAM_H + +#include +#include +#include +#include +#include + +class EthernetClientStream : public Stream +{ +public: + EthernetClientStream(Client &client, IPAddress localip, IPAddress ip, const char* host, uint16_t port); + int available(); + int read(); + int peek(); + void flush(); + size_t write(uint8_t); + void maintain(IPAddress localip); + +private: + IPAddress localip; + IPAddress ip; + const char* host; + uint16_t port; + Client &client; + bool connected; + uint32_t time_connect; + bool maintain(); + void stop(); +}; + +#endif diff --git a/libraries/Firmata/src/utility/firmataDebug.h b/libraries/Firmata/src/utility/firmataDebug.h new file mode 100644 index 00000000000..6e364b0c7b5 --- /dev/null +++ b/libraries/Firmata/src/utility/firmataDebug.h @@ -0,0 +1,14 @@ +#ifndef FIRMATA_DEBUG_H +#define FIRMATA_DEBUG_H + +#ifdef SERIAL_DEBUG + #define DEBUG_BEGIN(baud) Serial.begin(baud); while(!Serial) {;} + #define DEBUG_PRINTLN(x) Serial.println (x) + #define DEBUG_PRINT(x) Serial.print (x) +#else + #define DEBUG_BEGIN(baud) + #define DEBUG_PRINTLN(x) + #define DEBUG_PRINT(x) +#endif + +#endif /* FIRMATA_DEBUG_H */ diff --git a/libraries/LiquidCrystal/examples/setCursor/setCursor.ino b/libraries/LiquidCrystal/examples/setCursor/setCursor.ino index 4790b68b804..df75f7fe98c 100644 --- a/libraries/LiquidCrystal/examples/setCursor/setCursor.ino +++ b/libraries/LiquidCrystal/examples/setCursor/setCursor.ino @@ -7,7 +7,7 @@ can usually tell them by the 16-pin interface. This sketch prints to all the positions of the LCD using the - setCursor(0 method: + setCursor() method: The circuit: * LCD RS pin to digital pin 12 @@ -56,9 +56,9 @@ void loop() { // loop from ASCII 'a' to ASCII 'z': for (int thisLetter = 'a'; thisLetter <= 'z'; thisLetter++) { // loop over the columns: - for (int thisCol = 0; thisCol < numRows; thisCol++) { + for (int thisRow = 0; thisRow < numRows; thisRow++) { // loop over the rows: - for (int thisRow = 0; thisRow < numCols; thisRow++) { + for (int thisCol = 0; thisCol < numCols; thisCol++) { // set the cursor position: lcd.setCursor(thisCol, thisRow); // print the letter: