Skip to content

Commit e5c7508

Browse files
committed
Initial commit
1 parent 5fa2271 commit e5c7508

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+5001
-0
lines changed

README.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# PhyESPx <img src="https://user-images.githubusercontent.com/73055949/224374773-722d08f8-42a1-4fda-a30c-9cfbf2ab77b4.png" height="45" align="right">
2+
...is an open-source data acquisition system for MINT education. It is designed to be specifically robust, very easy to use, and low-cost while providing decent data rates as well as an extensive functionality. PhyESPx sensor kits can measure all kinds of physical parameters and transmit the acquired data to a web interface that can be accessed on tablets, laptops, or PCs. Multiple sensor kits can be used at the same time due to mesh-based (WiFi) data transmission, or can be combined within multi-axis charts for more complex experiments.
3+
4+
## How to use the sensor kit?
5+
A PhyESPx sensor kit consists of a <b><i>transmitter</i></b> and one or more <b><i>sensors</i></b>. To use a sensor, attach it to the transmitter via the built-in hardware interface locking mechanism. Turn on the transmitter and access the corresponding user interface via the QR code on the transmitter housing, which can be scanned with a smartphone or tablet, or the device hub, which is accessible via a local web service. Start data acquisition using the controls within the web interface or the buttons on the transmitter and watch your readings being displayed in real-time. To analyze or process the acquired data further, export it in various formats, such as .csv or .json.
6+
7+
## Something about the working principle...
8+
PhyESPx heavily relies on commercial sensor circuits for reading physical parameters. Those are available in a huge variety and often contain ADCs with I²C or SPI support, however, you may also have to manually install ADCs yourself. A PhyESPx sensor consists of the sensor circuit (with ADC), a little flash memory chip (containing the sensor ID for automatic sensor detection), and an interface connector (for electrical connection to the transmitter), which are fitted in a 3d-printed enclosure.
9+
10+
The PhyESPx transmitter is based on the ESP32 microcontroller, which supports the I²C and SPI protocols, has a built-in WiFi interface, and is powered by a LiPo battery (600mAh --> approx. 4:30 runtime). The transmitter is responsible for reading out the data from the sensor and wirelessly transmitting it to the connected user interfaces.
11+
12+
The web interfaces for the different sensors as well as the <b><i>device hub</i></b> are provided by a central web server. Once loaded, the <b><i>device hub</i></b> will search for PhyESPx transmitters that are available from the client device and allow to access their web interfaces (while displaying some information on the attached sensors). The web interface (which is sensor-specific) establishes a websocket connection to the transmitter and can be used to control and analyze the experiments.
13+
14+
## How to set it up?
15+
Further information for building and setting up the sensor kit can be found <a href="https://github.com/deebuggertech/PhyESPx/tree/main/how%202%20build">on this page</a>. You will need a very basic understanding of microelectronics, networks, and some tools (including soldering eqipment and a 3d printer). If you have any questions, <a href="mailto:dee.bugger.tech@gmail.com">I'm happy to help you</a>!
16+
17+
## Advantages of the System
18+
<b>PhyESPx is...</b>
19+
<ul>
20+
<li><b>easy to set up and handle</b> (simple control and connection management with a web UI)</li>
21+
<li><b>robust and failsafe</b> (can also be used by younger students)</li>
22+
<li><b>extremely inexpensive</b> (transmitter unit parts cost ≈ 15€)</li>
23+
<li><b>scalable</b> (several sensor kits can be operated in parallel within one network)</li>
24+
<li><b>cross-platform usable</b> (works with nearly all OSs and on laptops, tablets, phones)</li>
25+
<li><b>secure in terms of privacy</b> (there's no need to install an app on the client device)</li>
26+
<li><b>extendable</b> (you can develop new sensors according to your needs)</li>
27+
<li><b>open source</b> (here you go)</li>
28+
</ul>
29+
30+
## Any more questions?
31+
...feel free to <a href="mailto:dee.bugger.tech@gmail.com">contact me</a> or open an issue! Maybe the <a href="https://deebugger.de/projects/phyespx/">project website</a> can help?
32+
33+
## Legal stuff...
34+
35+
(1) The PhyESPx web interface contains structural and design elements from the <a href="https://github.com/phyphox">phyphox project</a>, which is distributed under the GNU 3.0 license.
36+
37+
(2) The PhyESPx web interface uses the javascript library <a href="http://chartjs.org">chart.js</a>. Chart.js is distributed under the MIT licence.

esp32/button.ino

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//PhyESPx
2+
//by deebugger.de
3+
4+
void setupButton(){
5+
USE_SERIAL.print("Setting up Hardware Trigger... ");
6+
pinMode(buttonPin, INPUT_PULLUP);
7+
attachInterrupt(digitalPinToInterrupt(buttonPin), interruptTriggered, FALLING);
8+
USE_SERIAL.println("Done!");
9+
}
10+
11+
long lastInterrupt;
12+
bool interruptFlag = false;
13+
14+
void interruptTriggered() {
15+
if(millis()-lastInterrupt > 500){
16+
interruptFlag = true; //flag workaround (ISR)
17+
lastInterrupt = millis();
18+
}
19+
}
20+
21+
void checkForButtonPressed(){
22+
if(interruptFlag){
23+
if(experimentStatus == "run"){
24+
USE_SERIAL.println("interrupt-pause");
25+
onPause();
26+
}else{
27+
USE_SERIAL.println("interrupt-run");
28+
onRun();
29+
}
30+
interruptFlag = false;
31+
}
32+
}

esp32/esp32.ino

+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
//PhyESPx
2+
//by deebugger.de
3+
4+
#include <WiFi.h>
5+
#include <WebSocketsServer.h>
6+
#include <ArduinoJson.h>
7+
8+
///* Software Version Code *///
9+
const char* SOFTWARE_VERSION = "3.7.1"; //(10.02.23)
10+
11+
///* Put Your 2.4GHz WiFi Credentials Here *///
12+
const char* WiFi_SSID = "YOUR_SSID";// <---MODIFY!
13+
const char* WiFi_PASSWD = "YOUR_PASSWD";// <---MODIFY!
14+
15+
///* Every Transmitter Has Its Own ID *///
16+
const byte transmitterID = 1;// <---MODIFY!
17+
18+
///* WiFi Network Settings *///
19+
IPAddress local_IP(192, 168, 178, 200+transmitterID); // <---Make Sure IP-Address Is Working!
20+
IPAddress gateway(192, 168, 178, 1);
21+
IPAddress subnet(255, 255, 255, 0);
22+
23+
///* Start Stop Button Pin *///
24+
const int buttonPin = 4;
25+
26+
DynamicJsonDocument doc(1024);
27+
WebSocketsServer webSocket = WebSocketsServer(81);
28+
29+
#define USE_SERIAL Serial //set to Serial for deeeeebugging
30+
31+
byte sensorID = 0; //1: illuminance sensor; 2: current sensor; 3: speed sensor; 4: voltage sensor;
32+
int sensorUpdateInterval = 100; //can be specified in sensor setup (default: 100ms delay --> 10Hz)
33+
String experimentStatus = "clear"; //"clear", "run", "pause"
34+
35+
36+
void setup() {
37+
USE_SERIAL.begin(115200);
38+
USE_SERIAL.setDebugOutput(true);
39+
USE_SERIAL.println();
40+
USE_SERIAL.println();
41+
USE_SERIAL.println();
42+
43+
setupStatusLED();
44+
45+
if (!WiFi.config(local_IP, gateway, subnet)) {
46+
USE_SERIAL.println("IP Config Failed");
47+
}
48+
49+
WiFi.begin(WiFi_SSID, WiFi_PASSWD);
50+
51+
USE_SERIAL.print("Connecting to WiFi");
52+
while(WiFi.status() != WL_CONNECTED){
53+
showConnectingAnimation();
54+
USE_SERIAL.print(".");
55+
}
56+
USE_SERIAL.println();
57+
58+
USE_SERIAL.print("Connected to ");
59+
USE_SERIAL.print(WiFi.SSID());
60+
USE_SERIAL.print(" with IP ");
61+
USE_SERIAL.println(WiFi.localIP());
62+
USE_SERIAL.print("Subnet Mask: ");
63+
USE_SERIAL.println(WiFi.subnetMask());
64+
USE_SERIAL.print("Gateway IP: ");
65+
USE_SERIAL.println(WiFi.gatewayIP());
66+
67+
USE_SERIAL.print("Setting up WebSocket Server... ");
68+
webSocket.begin();
69+
webSocket.onEvent(webSocketEvent);
70+
webSocket.enableHeartbeat(1000, 500, 2);
71+
USE_SERIAL.println("Done!");
72+
73+
setupButton();
74+
75+
setupAutoUpdate();
76+
77+
setupSensor();
78+
79+
setStatusLedIdle();
80+
}
81+
82+
double startTime, lastSessionDuration; //session parameters for "time" dataset in experiment
83+
84+
void onRun(){
85+
webSocket.broadcastTXT("{\"status\":\"run\"}");
86+
experimentStatus = "run";
87+
setStatusLedRunning();
88+
startTime = millis() - lastSessionDuration;
89+
}
90+
91+
void onPause(){
92+
webSocket.broadcastTXT("{\"status\":\"pause\"}");
93+
experimentStatus = "pause";
94+
lastSessionDuration = millis() - startTime;
95+
setStatusLedConnected();
96+
}
97+
98+
void onClear(){
99+
webSocket.broadcastTXT("{\"status\":\"clear\"}");
100+
experimentStatus = "clear";
101+
startTime = 0;
102+
lastSessionDuration = 0;
103+
setStatusLedConnected();
104+
}
105+
106+
long lastPingReceived; //runtime last ping received
107+
bool clientAvailable = false; //at least one client is available
108+
109+
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
110+
switch(type) {
111+
case WStype_CONNECTED:
112+
{
113+
webSocket.sendTXT(num, "{\"deviceID\":\""+String(transmitterID) +"\", \"sensorID\":\""+String(sensorID)+"\"}");
114+
webSocket.broadcastTXT("{\"status\":\""+String(experimentStatus)+"\"}");
115+
if (experimentStatus != "run") setStatusLedConnected();
116+
clientAvailable = true;
117+
}
118+
break;
119+
case WStype_TEXT:
120+
if(String((char *)payload).equals(String("ping"))){
121+
webSocket.broadcastTXT("pong");
122+
lastPingReceived = millis();
123+
if(!clientAvailable){ //for initial connection set
124+
setStatusLedConnected();
125+
clientAvailable = true;
126+
}
127+
128+
}else{
129+
DeserializationError error = deserializeJson(doc, payload);
130+
if (error) {
131+
USE_SERIAL.print(F("deserializeJson() failed: "));
132+
USE_SERIAL.println(error.f_str());
133+
}else{
134+
if(doc["set_status"]){
135+
experimentStatus = doc["set_status"].as<String>();
136+
if(experimentStatus.equals("clear")){
137+
onClear();
138+
}else if(experimentStatus.equals("run")){
139+
onRun();
140+
}else if(experimentStatus.equals("pause")){
141+
onPause();
142+
}
143+
}
144+
}
145+
}
146+
break;
147+
}
148+
}
149+
150+
long lastPackageSent;
151+
void loop() {
152+
153+
checkForAutoUpdate();
154+
checkForButtonPressed();
155+
checkForSensorChanged();
156+
157+
webSocket.loop();
158+
159+
if(experimentStatus.equals("run")){ //gather data
160+
if(millis()-lastPackageSent > sensorUpdateInterval){
161+
lastPackageSent = millis();//right at the start to exclude processing time
162+
doc.clear();
163+
doc["time"] = (millis()-startTime) / 1000;
164+
readSensor();
165+
String dataset;
166+
serializeJson(doc, dataset);
167+
webSocket.broadcastTXT(dataset);
168+
}
169+
}
170+
171+
///** Ping Handler **///
172+
if(millis() - lastPingReceived > 3500){ //ping timeout
173+
setStatusLedIdle();
174+
clientAvailable = false;
175+
}
176+
}

esp32/led.ino

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//PhyESPx
2+
//by deebugger.de
3+
4+
#include <Adafruit_NeoPixel.h>
5+
6+
Adafruit_NeoPixel pixel(1, 5, NEO_GRB + NEO_KHZ800);
7+
8+
void setupStatusLED(){
9+
pixel.begin();
10+
for(int i = 0; i < 15; i++){
11+
setStatusLedColor(i*5,0,0);
12+
delay(40);
13+
}
14+
}
15+
16+
void showConnectingAnimation(){
17+
for(int i = 15; i > 0; i--){
18+
setStatusLedColor(i*5,0,0);
19+
delay(40);
20+
}
21+
for(int i = 0; i < 15; i++){
22+
setStatusLedColor(i*5,0,0);
23+
delay(40);
24+
}
25+
}
26+
27+
void setStatusLedIdle(){
28+
setStatusLedColor(50,50,50);
29+
}
30+
31+
void setStatusLedConnected(){
32+
setStatusLedColor(0,0,80);
33+
}
34+
35+
void setStatusLedRunning(){
36+
setStatusLedColor(0,80,0);
37+
}
38+
39+
void setStatusLedColor(int r, int g, int b){
40+
pixel.setPixelColor(0, pixel.Color(r*2.55, g*2.55, b*2.55));
41+
pixel.show();
42+
}
43+
44+
//Definitely over complicated...

0 commit comments

Comments
 (0)