Compare commits

..

21 Commits

Author SHA1 Message Date
fff9dec208 add mechanical stuff 2022-11-12 22:17:07 -07:00
1ca5d7b208 resets activity timer after call end 2016-12-03 09:25:16 -07:00
bcbf835bcd restarts dialtone time when incoming call is refused 2016-12-02 23:28:06 -07:00
dff7c4745f allows incoming call to not be answered 2016-12-02 23:14:52 -07:00
0c703101a3 changes baudrate to 115200 2016-12-02 22:57:36 -07:00
66ef9685fa Adds GSM_BAUDRATE definition 2016-12-02 22:18:19 -07:00
21a352ed43 fixes sleeping. removes delay during sleep. reduces RSSI threshold 2016-12-02 22:06:48 -07:00
09075932a7 makes dialtone play for SLEEP_TIMEOUT 2016-12-02 21:37:20 -07:00
fed15da794 adds sleeping 2016-12-02 20:50:18 -07:00
eb5aee66bc changes RSSI threshold 2016-12-02 20:15:05 -07:00
e6597a726b rssi led fix 2016-12-02 20:10:42 -07:00
fd7491d8aa adds RSSI info 2016-12-02 20:09:01 -07:00
7f35b88b81 reformats code 2016-12-02 20:00:37 -07:00
aa1ed81a6f increases speaker volume 2016-12-02 19:50:15 -07:00
f830cd2251 removes remaining references to CHG_PIN 2016-12-02 19:45:26 -07:00
f748faa6e8 removes remaining usb debug stuff 2016-12-02 19:43:25 -07:00
34ae6a7f70 removes arduino controlled battery management 2016-12-02 19:41:37 -07:00
4bfe585457 makes low battery light work 2016-12-02 19:37:42 -07:00
5ea835ef74 should be correct keypad pinout 2016-11-29 21:18:46 -07:00
0e6bf815ca uploads and communicates over serial 2016-11-29 21:15:27 -07:00
eb947bff8c should change all pinouts to match v1.1 PCB (keypad pinout possibly wrong) 2016-11-29 20:45:52 -07:00
56 changed files with 344 additions and 259 deletions

View File

@ -1,249 +1,211 @@
#include "avr/sleep.h"
#include "Adafruit_FONA.h"
#include "Keypad.h"
////////////////////////////////
////////// PARAMETERS //////////
////////////////////////////////
// Arduino will wake from sleep when WAKE_PIN is pulled low. Connect any waking buttons to WAKE_PIN
#define WAKE_PIN 2
#define BUT_ANS A3
#define BUT_END A4
// RX/TX are in reference to the Arduino, not SIM800
#define GSM_RX 3
#define GSM_TX 11
#define GSM_RST A2
#define GSM_RING A5
// TIMEOUT_SLEEP is the time to stay awake from last activity until sleep (milliseconds)
#define TIMEOUT_SLEEP 6000
// Charging voltage thresholds. Will turn on charging at CHG_VLO and turn off charging at CHG_VHI (millivolts)
#define CHG_VLO 3900
#define CHG_VHI 4100
#define CHG_PIN A0
// Comment the following line to use HW serial for Fona and disable debugging information
#define USB_DEBUG
// Comment the following line to disable sleeping the Arduino
//#define SLEEP
// Keypad pinout
byte rowPins[4] = {9, 4, 5, 7};
byte colPins[3] = {8, 10, 6};
////////////////////////////////////
////////// END PARAMETERS //////////
////////////////////////////////////
char phoneNumber[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int phoneNumberLength = 0;
char keys[4][3] = {
{'1', '2', '3'},
{'4', '5', '6'},
{'7', '8', '9'},
{'*', '0', '#'}
};
unsigned long lastActiveTime;
bool dialtoneActive = false;
bool startDialtone = false;
#ifdef USB_DEBUG
#include "SoftwareSerial.h"
SoftwareSerial fonaSS = SoftwareSerial(GSM_RX, GSM_TX);
SoftwareSerial *fonaSerial = &fonaSS;
#else
HardwareSerial *fonaSerial = &Serial;
#endif
Adafruit_FONA fona = Adafruit_FONA(GSM_RST);
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, 4, 3 );
///////////////////////////////
////////// FUNCTIONS //////////
///////////////////////////////
void wakeFromSleep() {
sleep_disable();
detachInterrupt(WAKE_PIN);
#ifdef USB_DEBUG
Serial.println("Woke up");
#endif
startDialtone = true;
}
void goToSleep() {
#ifdef USB_DEBUG
Serial.println("Going to sleep");
#endif
sleep_enable();
attachInterrupt(WAKE_PIN, wakeFromSleep, LOW);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
cli();
sleep_bod_disable();
sei();
sleep_cpu();
}
void inCall() {
while (1) {
if ( !digitalRead(BUT_END) || fona.getCallStatus() < 3 ) { // End button pressed
fona.hangUp();
#ifdef USB_DEBUG
Serial.println("Hanging up");
#endif
break;
}
// numpad stuff
}
for ( int j = 0; j < phoneNumberLength; j++) {
phoneNumber[j] = 0;
}
phoneNumberLength = 0;
startDialtone = true;
}
void beginCall() {
#ifdef USB_DEBUG
Serial.println("Starting Call");
#endif
fona.sendCheckReply( F("AT+STTONE=0"), F("OK") ); // End dialtone
dialtoneActive = false;
if ( fona.callPhone( phoneNumber ) ) {
#ifdef USB_DEBUG
Serial.println("Call Started");
#endif
inCall();
}
#ifdef USB_DEBUG
Serial.println("Call ended");
#endif
}
void resumeDialtone() {
fona.sendCheckReply( F("AT+STTONE=1,20,30000" ), F("OK") ); // Start dialtone
dialtoneActive = true;
}
//////////////////////////////////
///// ARDUINO CORE FUNCTIONS /////
//////////////////////////////////
void setup() {
#ifdef USB_DEBUG
Serial.begin(9600);
Serial.println("Booted. Setting up");
#endif
pinMode( WAKE_PIN, INPUT_PULLUP );
pinMode( BUT_ANS, INPUT_PULLUP );
pinMode( BUT_END, INPUT_PULLUP );
pinMode( GSM_RST, OUTPUT );
pinMode( GSM_RING, INPUT_PULLUP );
pinMode( CHG_PIN, OUTPUT );
digitalWrite( GSM_RST, HIGH );
digitalWrite( CHG_PIN, HIGH );
fonaSerial->begin(4800);
if (! fona.begin(*fonaSerial)) {
#ifdef USB_DEBUG
Serial.println("Fona failed to respond");
#endif
while (1); //fona didn't start
}
while ( !fona.setAudio(FONA_EXTAUDIO) ) {}
#ifdef USB_DEBUG
Serial.println("Setup Complete");
#endif
startDialtone = true;
lastActiveTime = millis();
fona.sendCheckReply( F("AT+CLVL=20"), F("OK") ); // set dialtone volume
}
void loop() {
if ( !digitalRead(GSM_RING) ) {
while ( digitalRead(BUT_ANS) & !digitalRead(GSM_RING) ) delay(10); // Wait for answer button or end ring/call
if ( !digitalRead(BUT_ANS) ) {
fona.pickUp();
delay(100);
inCall();
}
}
if ( startDialtone ) {
resumeDialtone();
startDialtone = false;
}
// Read from keypad
char key = keypad.getKey();
if ( key != NO_KEY ) {
phoneNumber[ phoneNumberLength ] = key;
phoneNumberLength = phoneNumberLength + 1;
if ( dialtoneActive ) {
fona.sendCheckReply( F("AT+STTONE=0"), F("OK") ); // End dialtone
dialtoneActive = false;
delay(50);
}
fona.playDTMF( key ); // Play DTMF tone
lastActiveTime = millis();
#ifdef USB_DEBUG
int i;
for (i = 0; i < 15; i = i + 1) {
Serial.print(phoneNumber[i]);
Serial.print(", ");
}
Serial.println(phoneNumberLength);
#endif
// Check for complete phone number (including +1 country code)
if ( ( phoneNumberLength == 10 & phoneNumber[0] != '1' ) || phoneNumberLength > 10 ) {
beginCall();
}
}
// Clear stored phone number on press of end button
if ( !digitalRead(BUT_END) ) {
for ( int j = 0; j < phoneNumberLength; j++) {
phoneNumber[j] = 0;
}
phoneNumberLength = 0;
#ifdef USB_DEBUG
Serial.println( phoneNumberLength );
#endif
resumeDialtone();
}
uint16_t vbat;
fona.getBattVoltage(&vbat);
if ( vbat < CHG_VLO ) {
digitalWrite( CHG_PIN, HIGH );
} else if ( vbat > CHG_VHI ) {
digitalWrite( CHG_PIN, LOW );
}
#ifdef SLEEP
// Autoshutdown if inactive for extended period
// Typecast to long avoids "rollover" issues
if ( (long)(millis() - lastActiveTime) > TIMEOUT_SLEEP ) {
goToSleep();
}
#endif
}
#include "avr/sleep.h"
#include "Adafruit_FONA.h"
#include "Keypad.h"
////////////////////////////////
////////// PARAMETERS //////////
////////////////////////////////
#define LED_BAT_LOW 3
#define LED_NO_SERVICE 2
#define BUT_ANS A3
#define BUT_END A4
#define GSM_RST A2
#define GSM_RING A5
#define GSM_BAUDRATE 115200
// Low battery light threshold
#define CHG_VLO 3800
// Time in miliseconds to stop listening for keypad input
#define SLEEP_TIMEOUT 120000
// RSSI value below which No Service LED will light
#define RSSI_THRESHOLD 1
// Keypad pinout
byte rowPins[4] = {5, 10, 9, 7};
byte colPins[3] = {6, 4, 8};
////////////////////////////////////
////////// END PARAMETERS //////////
////////////////////////////////////
char phoneNumber[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int phoneNumberLength = 0;
char keys[4][3] = {
{'1', '2', '3'},
{'4', '5', '6'},
{'7', '8', '9'},
{'*', '0', '#'}
};
unsigned long lastActiveTime;
bool dialtoneActive = false;
bool startDialtone = false;
bool awake = true;
HardwareSerial *fonaSerial = &Serial;
Adafruit_FONA fona = Adafruit_FONA(GSM_RST);
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, 4, 3 );
///////////////////////////////
////////// FUNCTIONS //////////
///////////////////////////////
void inCall() {
while (1) {
if ( !digitalRead(BUT_END) || fona.getCallStatus() < 3 ) { // End button pressed
fona.hangUp();
break;
}
// numpad stuff
}
clearPhoneNumber();
startDialtone = true;
lastActiveTime = millis();
delay(100);
}
void beginCall() {
fona.sendCheckReply( F("AT+STTONE=0"), F("OK") ); // End dialtone
dialtoneActive = false;
if ( fona.callPhone( phoneNumber ) ) {
inCall();
}
}
void resumeDialtone() {
fona.sendCheckReply( ("AT+STTONE=1,20," + String(SLEEP_TIMEOUT)).c_str(), "OK" ); // Start dialtone
dialtoneActive = true;
}
void clearPhoneNumber() {
for ( int j = 0; j < phoneNumberLength; j++) {
phoneNumber[j] = 0;
}
phoneNumberLength = 0;
}
void goToSleep() {
digitalWrite( LED_NO_SERVICE, LOW );
digitalWrite( LED_BAT_LOW, LOW );
awake = false;
}
//////////////////////////////////
///// ARDUINO CORE FUNCTIONS /////
//////////////////////////////////
void setup() {
pinMode( BUT_ANS, INPUT_PULLUP );
pinMode( BUT_END, INPUT_PULLUP );
pinMode( GSM_RST, OUTPUT );
pinMode( GSM_RING, INPUT_PULLUP );
pinMode( LED_BAT_LOW, OUTPUT);
pinMode( LED_NO_SERVICE, OUTPUT);
digitalWrite( LED_BAT_LOW, HIGH );
digitalWrite( LED_NO_SERVICE, HIGH );
digitalWrite( GSM_RST, HIGH );
fonaSerial->begin(GSM_BAUDRATE);
if (! fona.begin(*fonaSerial)) {
while (1); //fona didn't start
digitalWrite( LED_BAT_LOW, HIGH );
}
startDialtone = true;
lastActiveTime = millis();
fona.sendCheckReply( F("AT+CLVL=100"), F("OK") ); // set volume
digitalWrite( LED_BAT_LOW, LOW );
digitalWrite( LED_NO_SERVICE, LOW );
}
void loop() {
if ( awake ) {
// Handle Incoming Call
if ( !digitalRead(GSM_RING) ) {
while ( digitalRead(BUT_ANS) & digitalRead(BUT_END) & !digitalRead(GSM_RING) ) delay(10); // Wait for answer button or end ring/call
if ( !digitalRead(BUT_ANS) ) {
fona.pickUp();
delay(100);
inCall();
} else if( !digitalRead(BUT_END) ) {
fona.hangUp();
clearPhoneNumber();
startDialtone = true;
delay(100);
resumeDialtone();
lastActiveTime = millis();
}
}
// Begin dialtone if necessary
if ( startDialtone ) {
resumeDialtone();
startDialtone = false;
}
// Read keypad input
char key = keypad.getKey();
if ( key != NO_KEY ) {
phoneNumber[ phoneNumberLength ] = key;
phoneNumberLength = phoneNumberLength + 1;
if ( dialtoneActive ) {
fona.sendCheckReply( F("AT+STTONE=0"), F("OK") ); // End dialtone
dialtoneActive = false;
delay(100);
}
fona.playDTMF( key ); // Play DTMF tone
lastActiveTime = millis();
// Check for complete phone number (including +1 country code)
if ( ( phoneNumberLength == 10 & phoneNumber[0] != '1' ) || phoneNumberLength > 10 ) {
beginCall();
}
}
// Clear stored phone number on press of end button
if ( !digitalRead(BUT_END) ) {
clearPhoneNumber();
resumeDialtone();
}
// Indicator LEDs
uint16_t vbat;
fona.getBattVoltage(&vbat);
if ( vbat < CHG_VLO ) {
digitalWrite( LED_BAT_LOW, HIGH );
} else {
digitalWrite( LED_BAT_LOW, LOW );
}
uint8_t rssi;
rssi = fona.getRSSI();
if ( rssi < RSSI_THRESHOLD || rssi == 99 ) {
digitalWrite( LED_NO_SERVICE, HIGH );
} else {
digitalWrite( LED_NO_SERVICE, LOW );
}
// If inactive, sleep
if ( (long)(millis() - lastActiveTime) > SLEEP_TIMEOUT ) {
goToSleep();
}
} else {
// sleeping
if ( !digitalRead( BUT_ANS ) || !digitalRead( BUT_END ) || !digitalRead( GSM_RING ) ) {
lastActiveTime = millis();
awake = true;
}
}
}

View File

@ -1,10 +1,10 @@
# Hohm-Phone
## Hardware Connections
-->|-- Indicates diode
WAKE_PIN -->|-- GSM_RING --- RI(Fona)
WAKE_PIN -->|-- BUT_ANS ---- Button --- GND
WAKE_PIN -->|-- BUT_END ---- Button --- GND
BUT_ANS and BUT_END have pull up resistors
# Hohm-Phone
## Hardware Connections
-->|-- Indicates diode
WAKE_PIN -->|-- GSM_RING --- RI(Fona)
WAKE_PIN -->|-- BUT_ANS ---- Button --- GND
WAKE_PIN -->|-- BUT_END ---- Button --- GND
BUT_ANS and BUT_END have pull up resistors

BIN
hardware/Assem2.SLDASM Normal file

Binary file not shown.

BIN
hardware/BOM_1v1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
hardware/Basepic.SLDDRW Normal file

Binary file not shown.

View File

@ -0,0 +1,54 @@
@misc{ wiki:DTMF,
author = "Wikipedia",
title = "Dual-tone multi-frequency signaling",
year = "2016",
url = "https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling",
note = "[Online; accessed 27-August-2016]"
}
@misc{ wiki:Goertzel,
author = "Wikipedia",
title = "Goertzel algorithm",
year = "2016",
url = "https://en.wikipedia.org/wiki/Goertzel_algorithm",
note = "[Online; accessed 27-August-2016]"
}
@misc{ wiki:FIR,
author = "Wikipedia",
title = "Finite impulse response",
year = "2016",
url = "https://en.wikipedia.org/wiki/Finite_impulse_response",
note = "[Online; accessed 27-August-2016]"
}
@misc{ wiki:IIR,
author = "Wikipedia",
title = "Infinite impulse response",
year = "2016",
url = "https://en.wikipedia.org/wiki/Infinite_impulse_response",
note = "[Online; accessed 27-August-2016]"
}
@misc{ wiki:Nyquist-Shannon,
author = "Wikipedia",
title = "NyquistShannon sampling theorem",
year = "2016",
url = "https://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem",
note = "[Online; accessed 27-August-2016]"
}
@misc{ wiki:Z-transform,
author = "Wikipedia",
title = "Z-transform",
year = "2016",
url = "https://en.wikipedia.org/wiki/Z-transform",
note = "[Online; accessed 27-August-2016]"
}
@article{ DTMF_genave,
author = "Genave.com",
title = "DTMF Explained",
url = "http://www.genave.com/dtmf.htm",
note = "[Online; accessed 27-August-2016]"
}

BIN
hardware/DTMF_Research.pdf Normal file

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,66 @@
\documentclass[12pt]{article}
\usepackage[margin=1in]{geometry}
\usepackage{parskip}
\usepackage{cite}
\usepackage{url}
\usepackage{arydshln}
\usepackage{amsmath}
\begin{document}
\title{Dual-Tone Multi-Frequency Signaling}
\author{Brendan Haines}
\maketitle
\section{Encoding}
Dual-Tone Multi Frequency Signaling uses audible tones as opposed to digital signals due to the nature of the mediums they are broadcast over. Since DTMF is often used with radio phones as well as traditional "landline" telephones, and these technologies are designed to carry voice well, staying within the same frequency range reduces complexity and expense of specialized equipment at both the transmitting and receiving stations. Often, the speaker output of a radio can simply be plugged into a decoder with no additional specialized hardware, resulting in lower equipment costs\cite{DTMF_genave}.
\subsection{Frequencies\cite{wiki:DTMF}}
\begin{tabular}{|c c c : c | l |}
\hline
1 & 2 & 3 & A & 687 Hz \\
4 & 5 & 6 & B & 770 Hz\\
7 & 8 & 9 & C & 852 Hz\\
$\ast$ & 0 & \# & D & 941 Hz\\
\hline
1209 Hz & 1336 Hz & 1477 Hz & 1633 Hz \\
\cline{1-4}
\end{tabular}
The A, B, C, and D keys are omitted on most handsets however are often used for automation purposes for triggering of remote functions such as controling an amateur radio repeater during an active phone call\cite{wiki:DTMF}.
\subsection{Timing}
DTF uses a "mark" (amplitude $\neq$ 0) followed by a "space" (amplitude = 0). Timing can vary widely depending on the system being used. For example, when using a manual encoder such as an ordinary telephone each button press will create a mark and the time between presses will be spaces. Higher speeds can be achieved using automatic or "store and forward" DTMF encoders. Motorola uses a standard of 250ms mark and 250ms space, and other systems include 40ms/20ms or 20ms/20ms mark and space respectively\cite{DTMF_genave}.
\section{Decoding}
Originally decoded using tuned filter banks, however DSP now dominates decoding and the Goertzel algorthim is often used\cite{wiki:DTMF}.
\subsection{Goertzel Algorithm}
The Goertzel algorithm or Goertzel filter uses the Discrete Fourier Transform (DFT) to evaluate frequency content of a signal at specific frequencies very efficiently. Although the Fast Fourier Transform (FFT) is more efficient for analyzing a complete spectrum of frequency, in the case where only a few frequencies are relevant (such as the 8 DTMF frequencies), the Goertzel algorithm is more numerically efficient. It works well even on small processors in embedded applications\cite{wiki:Goertzel}
Where $x[n]$ is the $n^{th}$ sample and $\omega _{0}$ is frequency in radians per sample, an intermediate sequence $s[n]$:
$$s[-2]=s[-1]=0$$
\begin{equation} \label{eqn:stage_1_goertzel}
s[n] = x[n] + 2 cos(\omega _{0})s[n-1] - s[n-2]
\end{equation}
The second stage of the Goertzel filter applies to $s[n]$, producing output sequence $y[n]$:
\begin{equation} \label{eqn:stage_2_goertzel}
y[n] = s[n] - e^{-j\omega _{0}}s[n-1]
\end{equation}
A Z-transform converts a discrete-time signal to a complex frequency domain\cite{wiki:Z-transform}. It can be applied to equations \ref{eqn:stage_1_goertzel} and \ref{eqn:stage_2_goertzel} respectively:
\begin{align} \label{eqn:z_goertzel}
\frac{S(z)}{X(z)} &= \frac{1}{1-2cos(\omega _{0})z^{-1} + z^{-2}}\nonumber\\
&= \frac{1}{(1-e^{+j\omega _{0}} z^{-1})(1 - e^{-j\omega _{0}}z^{-1})}\\
\frac{Y(z)}{S(z)} &= 1 - e^{-j\omega _{0}} z^{-1}
\end{align}
The combined transfer function of the cascade of the two filters:
\begin{align} \label{eqn:z_combined_goertzel}
\frac{S(z)}{X(z)}\frac{Y(z)}{S(z)} = \frac{Y(z)}{X(z)} &= \frac{(1-e^{-j\omega _{0}}z^{-1})}{(1-e^{+j\omega _{0}} z^{-1})(1 - e^{-j\omega _{0}}z^{-1})}\nonumber\\
&= \frac{1}{(1-e^{+j\omega _{0}} z^{-1})}
\end{align}
\bibliography{DTMF_Research}{}
\bibliographystyle{IEEEtran}
\end{document}

View File

@ -0,0 +1,3 @@
@article{ wikipedia_DTMF
title="DTMF"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

BIN
hardware/Home Base.sldprt Normal file

Binary file not shown.

BIN
hardware/P1.SLDPRT Normal file

Binary file not shown.

BIN
hardware/P1.STL Normal file

Binary file not shown.

BIN
hardware/P1Inductor.SLDPRT Normal file

Binary file not shown.

BIN
hardware/P1Keypad.SLDPRT Normal file

Binary file not shown.

BIN
hardware/P1Speaker.SLDPRT Normal file

Binary file not shown.

BIN
hardware/P2.SLDPRT Normal file

Binary file not shown.

BIN
hardware/P2.STL Normal file

Binary file not shown.

BIN
hardware/P2_casual.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

BIN
hardware/P2_casual_g.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

BIN
hardware/P2_left.SLDPRT Normal file

Binary file not shown.

BIN
hardware/P2_left.STL Normal file

Binary file not shown.

BIN
hardware/P2_right.SLDPRT Normal file

Binary file not shown.

BIN
hardware/P2_right.STL Normal file

Binary file not shown.

BIN
hardware/P3 - Part 2.stl Normal file

Binary file not shown.

BIN
hardware/P3 - base.SLDPRT Normal file

Binary file not shown.

BIN
hardware/P3.SLDPRT Normal file

Binary file not shown.

BIN
hardware/P7.SLDPRT Normal file

Binary file not shown.

BIN
hardware/P7_assembly.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

BIN
hardware/P8.SLDPRT Normal file

Binary file not shown.

BIN
hardware/P8_left.SLDDRW Normal file

Binary file not shown.

BIN
hardware/P8_left.SLDPRT Normal file

Binary file not shown.

BIN
hardware/P8_left.STL Normal file

Binary file not shown.

BIN
hardware/P8_right.SLDPRT Normal file

Binary file not shown.

BIN
hardware/P8_right.STL Normal file

Binary file not shown.

BIN
hardware/P9.SLDPRT Normal file

Binary file not shown.

BIN
hardware/P9_left.STL Normal file

Binary file not shown.

BIN
hardware/P9_right.STL Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

BIN
hardware/button cap.SLDPRT Normal file

Binary file not shown.

BIN
hardware/final_assem.SLDASM Normal file

Binary file not shown.

BIN
hardware/final_bottom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

BIN
hardware/final_docked.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

BIN
hardware/final_top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

BIN
hardware/hohm base.pdf Normal file

Binary file not shown.

BIN
hardware/led.SLDPRT Normal file

Binary file not shown.

BIN
hardware/p10.SLDPRT Normal file

Binary file not shown.

BIN
hardware/p10_left.STL Normal file

Binary file not shown.

BIN
hardware/p10_right.STL Normal file

Binary file not shown.

Binary file not shown.

BIN
hardware/p11.SLDPRT Normal file

Binary file not shown.

BIN
hardware/p12.SLDPRT Normal file

Binary file not shown.