├── .gitignore ├── ArduinoDAQ └── ArduinoDAQ.pde ├── README.md ├── arduino.py ├── daq.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | Lib/** 3 | Scripts/** 4 | __pycache__/** 5 | Include/** 6 | -------------------------------------------------------------------------------- /ArduinoDAQ/ArduinoDAQ.pde: -------------------------------------------------------------------------------- 1 | //// ArduinoDAQ 2 | // Kevin Hughes 2012 3 | 4 | //// Constants 5 | int d = 1; 6 | 7 | void setup() { 8 | 9 | // All pins to input 10 | pinMode(A0, INPUT); 11 | pinMode(A1, INPUT); 12 | pinMode(A2, INPUT); 13 | pinMode(A3, INPUT); 14 | pinMode(A4, INPUT); 15 | pinMode(A5, INPUT); 16 | 17 | // Init Serial 18 | Serial.begin(115200); 19 | 20 | }// end setup 21 | 22 | void loop() { 23 | 24 | if(Serial.available()) { 25 | 26 | int signal = Serial.read(); 27 | 28 | if(signal == 119) { 29 | 30 | Serial.println( analogRead(A0) ); delayMicroseconds(d); 31 | Serial.println( analogRead(A1) ); delayMicroseconds(d); 32 | Serial.println( analogRead(A2) ); delayMicroseconds(d); 33 | Serial.println( analogRead(A3) ); delayMicroseconds(d); 34 | Serial.println( analogRead(A4) ); delayMicroseconds(d); 35 | Serial.println( analogRead(A5) ); delayMicroseconds(d); 36 | 37 | 38 | // Testing 39 | /* 40 | Serial.println( "A0" ); delayMicroseconds(d); 41 | Serial.println( "A1" ); delayMicroseconds(d); 42 | Serial.println( "A2" ); delayMicroseconds(d); 43 | Serial.println( "A3" ); delayMicroseconds(d); 44 | Serial.println( "A4" ); delayMicroseconds(d); 45 | Serial.println( "A5" ); delayMicroseconds(d); 46 | */ 47 | 48 | }//end if 49 | }// end if 50 | }// end loop 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ArduinoDAQ 2 | ========== 3 | 4 | by: Kevin Hughes, June 2013 5 | 6 | updated by: Victor H. Martin, June 2018 7 | 8 | 9 | ![usage](http://kevinhughes.ca/images/posts/arduino_daq_test.png) 10 | 11 | Description 12 | ----------- 13 | Usage: 14 | load the ArduinoDAQ program on the Arduino then run the main python program: 15 | python daq.py 16 | 17 | An Arduino program which reads the analog pins and sends the data over the serial port to the computer. A GUI written in python is provided for viewing the data from the arduino in real-time and saving the data to hard disk as csv. 18 | 19 | 20 | Requirements 21 | ------------ 22 | Python 3 23 | 24 | Dependencies file requirement.txt is provided for easy installation of dependencies using PiP. If you have pip installed 25 | run the next command in python command line: 26 | 27 | pip install -r requirements.txt 28 | 29 | The main packages required (dependencies of these libraries would be automatically installed) are the following: 30 | 31 | wx python 32 | sudo apt-get install python-wx 33 | 34 | py serial 35 | sudo apt-get install python-serial 36 | 37 | matplotlib 38 | sudo apt-get install python-matplotlib 39 | 40 | 41 | OSX Install Instructions: 42 | http://rwsarduino.blogspot.ca/2013/07/python-arduino-daq-on-mac.html 43 | -------------------------------------------------------------------------------- /arduino.py: -------------------------------------------------------------------------------- 1 | import serial 2 | 3 | class Arduino(object): 4 | """ Class that handles acquiring the serial data 5 | from the arduino for Data Acquisiton 6 | """ 7 | 8 | def __init__(self, usbport, baud): 9 | self.ser = serial.Serial( 10 | port=usbport, 11 | baudrate=baud, 12 | bytesize=serial.EIGHTBITS, 13 | parity=serial.PARITY_NONE, 14 | stopbits=serial.STOPBITS_ONE, 15 | timeout=1, 16 | xonxoff=0, 17 | rtscts=0, 18 | interCharTimeout=None 19 | ) 20 | #print "Serial Connected" 21 | 22 | def poll(self): 23 | self.ser.flush() #flush before sending signal 24 | self.ser.write("w".encode('ascii')) #send signal telling Arduino to send data 25 | 26 | # now read all lines sent by the Arduino 27 | data = [] 28 | 29 | # read analog channels 30 | for i in range(0,5+1): # now read the 6 analog channel 31 | data.append( int(self.ser.readline()[0:-2]) ) #this line will crash when running "test" because it sends strings not ints 32 | 33 | return data 34 | -------------------------------------------------------------------------------- /daq.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, platform 4 | import time 5 | import numpy as np 6 | import wx 7 | import matplotlib 8 | matplotlib.use('WXAgg') 9 | from matplotlib.figure import Figure 10 | from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigCanvas 11 | from arduino import Arduino 12 | 13 | class MainWindow(wx.Frame): 14 | """ Main frame of the application 15 | """ 16 | 17 | title = 'Data Acquisition' 18 | 19 | 20 | def __init__(self): 21 | wx.Frame.__init__(self, None, title=self.title, size=(650,570)) 22 | 23 | if platform.system() == 'Windows': 24 | arduino_port = 'COM4' 25 | else: 26 | arduino_port = '/dev/ttyACM0' 27 | 28 | # Try Arduino 29 | try: 30 | self.arduino = Arduino(arduino_port, 115200) 31 | except: 32 | msg = wx.MessageDialog(self, 'Unable to connect to arduino. Check port or connection.', 'Error', wx.OK | wx.ICON_ERROR) 33 | msg.ShowModal() == wx.ID_YES 34 | msg.Destroy() 35 | 36 | self.create_main_panel() 37 | 38 | self.recording = False 39 | self.output_file = '' 40 | 41 | time.sleep(1) 42 | 43 | # Timer 44 | self.timer = wx.Timer(self) 45 | self.Bind(wx.EVT_TIMER, self.on_timer, self.timer) 46 | self.rate = 500 47 | self.timer.Start(self.rate) 48 | 49 | 50 | def create_main_panel(self): 51 | 52 | # Panels 53 | self.plots_panel = wx.Panel(self) 54 | self.record_panel = wx.Panel(self) 55 | 56 | # Init Plots 57 | self.init_plots() 58 | self.PlotsCanvas = FigCanvas(self.plots_panel, wx.ID_ANY, self.fig) 59 | 60 | # Recording 61 | self.btn_record = wx.Button(self.record_panel, wx.ID_ANY, label="Record", pos=(500,20), size=(100,30)) 62 | self.Bind(wx.EVT_BUTTON, self.on_btn_record, self.btn_record) 63 | self.Bind(wx.EVT_UPDATE_UI, self.on_update_btn_record, self.btn_record) 64 | 65 | self.txt_output_file = wx.TextCtrl(self.record_panel, wx.ID_ANY, pos=(20,20), size=(440,30)) 66 | 67 | # Sizers 68 | vertical = wx.BoxSizer(wx.VERTICAL) 69 | vertical.Add(self.plots_panel, 0 , wx.ALL, 5) 70 | vertical.Add(self.record_panel, 0 , wx.ALL, 5) 71 | 72 | # Layout 73 | self.SetAutoLayout(True) 74 | self.SetSizer(vertical) 75 | self.Layout() 76 | 77 | 78 | def init_plots(self): 79 | self.plotMem = 50 # how much data to keep on the plot 80 | self.plotData = [[0] * (6)] * self.plotMem # mem storage for plot 81 | 82 | self.fig = Figure((8,6)) 83 | self.fig.subplots_adjust(hspace=.5) # sub plot spacing 84 | 85 | self.axes = [] # subplot list 86 | for i in range(1,7): 87 | self.axes.append(self.fig.add_subplot(3,2,i, xticks=[], yticks=[0, 500, 1000])) 88 | 89 | 90 | def poll(self): 91 | self.dataRow = self.arduino.poll() 92 | # self.dataRow = (np.random.rand(6)*1000).tolist() 93 | # print self.dataRow 94 | 95 | def save(self): 96 | file = open(self.output_file, 'a') 97 | for i in range(0,5): 98 | file.write(str(self.dataRow[i]) + ',') 99 | file.write(str(self.dataRow[5]) + '\n') 100 | file.close() 101 | 102 | 103 | def draw(self): 104 | self.plotData.append(self.dataRow) # adds to the end of the list 105 | self.plotData.pop(0) # remove the first item in the list, ie the oldest 106 | 107 | # Plot 108 | x = np.asarray(self.plotData) 109 | 110 | for (i, ax) in enumerate(self.axes): 111 | ax.plot(range(0,self.plotMem), x[:,i],'k') 112 | ax.set_title('CH A'+str(i)) 113 | ax.set_ylim(0,1000) 114 | ax.set_yticks([0, 250, 500, 750, 1000]) 115 | ax.set_xticks([]) 116 | ax.hold(False) 117 | 118 | # draw 119 | self.PlotsCanvas.draw() 120 | 121 | 122 | def on_timer(self, event): 123 | self.poll() 124 | 125 | if self.recording: 126 | self.save() 127 | 128 | self.draw() 129 | 130 | 131 | def on_update_btn_record(self, event): 132 | label = "Stop" if self.recording else "Record" 133 | self.btn_record.SetLabel(label) 134 | 135 | def on_btn_record(self, event): 136 | 137 | # pause timer 138 | self.timer.Stop() 139 | 140 | # switch state 141 | self.recording = not self.recording 142 | 143 | # if recording 144 | if self.recording: 145 | 146 | # check that a dir has been specified 147 | if self.txt_output_file.IsEmpty(): 148 | 149 | msg = wx.MessageDialog(self, 'Specify the Output Directory', 'Error', wx.OK | wx.ICON_ERROR) 150 | msg.ShowModal() == wx.ID_YES 151 | msg.Destroy() 152 | 153 | self.recording = False 154 | 155 | else: 156 | self.output_file = self.txt_output_file.GetLineText(0) 157 | 158 | # check if file exists - ie may be saving over data 159 | if os.path.isfile(self.output_file): 160 | msg = wx.MessageDialog(self, 'Output File %s Exists - Overwrite Data?' %self.output_file, 'Yes or No', wx.YES_NO | wx.ICON_QUESTION) 161 | result = msg.ShowModal() == wx.ID_YES 162 | msg.Destroy() 163 | 164 | # overwrite the data 165 | if result == True: 166 | 167 | # delete the file 168 | os.remove(self.output_file) 169 | 170 | # make new file 171 | open(self.output_file, 'w').close() 172 | 173 | # do not overwrite the data 174 | else: # result == False 175 | self.recording = False 176 | self.txt_output_file.SetFocus() 177 | 178 | # no file so make one 179 | else: 180 | open(self.output_file, 'w').close() 181 | 182 | # un pause timer 183 | self.timer.Start(self.rate) 184 | 185 | return 186 | 187 | 188 | def on_exit(self, event): 189 | self.Destroy() 190 | 191 | 192 | if __name__ == '__main__': 193 | app = wx.App() 194 | app.frame = MainWindow() 195 | app.frame.Show() 196 | app.MainLoop() 197 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cycler==0.10.0 2 | kiwisolver==1.0.1 3 | matplotlib==2.2.2 4 | numpy==1.14.2 5 | pyparsing==2.2.0 6 | Pypubsub==4.0.0 7 | pyserial==3.4 8 | python-dateutil==2.7.2 9 | pytz==2018.4 10 | six==1.11.0 11 | wxPython==4.0.2a1.dev3717+d4bd2fe 12 | --------------------------------------------------------------------------------