Showing preview only (696K chars total). Download the full file or copy to clipboard to get everything.
Repository: robertoostenveld/arduino
Branch: main
Commit: 50b80b8019ad
Files: 263
Total size: 631.7 KB
Directory structure:
gitextract_yp2dqrl9/
├── .gitignore
├── .gitmodules
├── PulseSensor_v0/
│ ├── AllSerialHandling.cpp
│ ├── Interrupt.cpp
│ ├── PulseSensor_v0.ino
│ ├── README.md
│ └── Timer_Interrupt_Notes.cpp
├── PulseSensor_v1/
│ ├── PulseSensor_v1.ino
│ └── README.md
├── PulseSensor_v2/
│ ├── Interrupt.cpp
│ ├── PulseSensor_v2.ino
│ └── README.md
├── README.md
├── bitsi/
│ └── bitsi.ino
├── blenderdefender/
│ └── blenderdefender.ino
├── digispark_skateboard/
│ ├── README.md
│ ├── colorspace.cpp
│ ├── colorspace.h
│ ├── digispark_skateboard.ino
│ ├── neopixel_mode.cpp
│ └── neopixel_mode.h
├── eegsynth_cvgate_mcp4725/
│ ├── README.md
│ └── eegsynth_cvgate_mcp4725.ino
├── eegsynth_cvgate_mcp4822/
│ └── eegsynth_cvgate_mcp4822.ino
├── eegsynth_devirtualizer/
│ └── eegsynth_devirtualizer.ino
├── eegsynth_usbdmxpro/
│ ├── COPYING.txt
│ └── eegsynth_usbdmxpro.ino
├── esp32_3wd_servo/
│ ├── README.md
│ └── esp32_3wd_servo.ino
├── esp32_3wd_stepper/
│ ├── README.md
│ ├── blink_led.cpp
│ ├── blink_led.h
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ ├── style.css
│ │ ├── waypoints.html
│ │ ├── waypoints1.csv
│ │ ├── waypoints2.csv
│ │ ├── waypoints3.csv
│ │ ├── waypoints4.csv
│ │ ├── waypoints5.csv
│ │ ├── waypoints6.csv
│ │ ├── waypoints7.csv
│ │ └── waypoints8.csv
│ ├── esp32_3wd_stepper.ino
│ ├── parseosc.cpp
│ ├── parseosc.h
│ ├── stepper.cpp
│ ├── stepper.h
│ ├── util.cpp
│ ├── util.h
│ ├── waypoints.cpp
│ ├── waypoints.h
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp32_config_webinterface_v5/
│ ├── README.md
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ └── style.css
│ └── esp32_config_webinterface_v5.ino
├── esp32_config_webinterface_v6/
│ ├── README.md
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ └── style.css
│ ├── esp32_config_webinterface_v6.ino
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp32_config_webinterface_v7/
│ ├── README.md
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ └── style.css
│ ├── esp32_config_webinterface_v7.ino
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp32_exgpill/
│ ├── README.md
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ └── style.css
│ ├── esp32_exgpill.ino
│ ├── fieldtrip_buffer.cpp
│ ├── fieldtrip_buffer.h
│ ├── rgb_led.cpp
│ ├── rgb_led.h
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp32_inmp441/
│ ├── RunningStat.h
│ ├── esp32_inmp441.ino
│ └── util.h
├── esp32_sph0645/
│ ├── RunningStat.h
│ ├── compress.cpp
│ └── esp32_sph0645.ino
├── esp32c6_feeder/
│ ├── README.md
│ └── esp32c6_feeder.ino
├── esp8266_12v_trigger/
│ └── esp8266_12v_trigger.ino
├── esp8266_ad8232_ecg/
│ ├── README.md
│ ├── blink_led.cpp
│ ├── blink_led.h
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ ├── style.css
│ │ └── update.html
│ ├── esp8266_ad8232_ecg.ino
│ ├── fieldtrip_buffer.cpp
│ ├── fieldtrip_buffer.h
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp8266_artnet_bci/
│ └── esp8266_artnet_bci.ino
├── esp8266_artnet_neopixel/
│ ├── README.md
│ ├── colorspace.cpp
│ ├── colorspace.h
│ ├── data/
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ ├── style.css
│ │ └── update.html
│ ├── esp8266_artnet_neopixel.ino
│ ├── font8x8_basic.h
│ ├── neopixel_mode.cpp
│ ├── neopixel_mode.h
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp8266_config_spiffs/
│ ├── data/
│ │ └── config.json
│ └── esp8266_config_spiffs.ino
├── esp8266_config_webinterface/
│ ├── README.md
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ ├── style.css
│ │ └── update.html
│ └── esp8266_config_webinterface.ino
├── esp8266_fan_control/
│ └── esp8266_fan_control.ino
├── esp8266_fieldtrip_buffer/
│ ├── esp8266_fieldtrip_buffer.ino
│ ├── fieldtrip_buffer.cpp
│ └── fieldtrip_buffer.h
├── esp8266_imu_osc/
│ ├── I2Cscan.cpp
│ ├── I2Cscan.h
│ ├── README.md
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ ├── style.css
│ │ └── update.html
│ ├── esp8266_imu_osc.ino
│ ├── rgb_led.cpp
│ ├── rgb_led.h
│ ├── tca9548a.cpp
│ ├── tca9548a.h
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp8266_inmp411/
│ ├── RunningStat.h
│ └── esp8266_inmp411.ino
├── esp8266_p1_thingspeak/
│ └── esp8266_p1_thingspeak.ino
├── esp8266_polar_wearlink/
│ ├── README.md
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ ├── style.css
│ │ └── update.html
│ ├── esp8266_polar_wearlink.ino
│ ├── rgb_led.cpp
│ ├── rgb_led.h
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp8266_pulse_sensor/
│ ├── README.md
│ ├── blink_led.cpp
│ ├── blink_led.h
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ ├── style.css
│ │ └── update.html
│ ├── esp8266_pulse_sensor.ino
│ ├── fieldtrip_buffer.cpp
│ ├── fieldtrip_buffer.h
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp8266_thingspeak/
│ └── esp8266_thingspeak.ino
├── m5dial_midi/
│ ├── colormap.h
│ └── m5dial_midi.ino
├── m5nanoc6_angle8_midi/
│ ├── colormap.h
│ └── m5nanoc6_angle8_midi.ino
├── m5nanoc6_encoder8_midi/
│ ├── colormap.h
│ └── m5nanoc6_encoder8_midi.ino
├── rfm12b_recv_xxxx/
│ ├── README.md
│ └── rfm12b_recv_xxxx.ino
├── rfm12b_send_am2301/
│ ├── README.md
│ └── rfm12b_send_am2301.ino
├── rfm12b_send_bmp085/
│ ├── README.md
│ └── rfm12b_send_bmp085.ino
├── rfm12b_send_cny70/
│ ├── README.md
│ └── rfm12b_send_cny70.ino
├── rfm12b_send_ds18b20/
│ ├── README.md
│ └── rfm12b_send_ds18b20.ino
├── rfm12b_send_lm35/
│ ├── README.md
│ └── rfm12b_send_lm35.ino
├── rfm12b_send_random/
│ ├── README.md
│ └── rfm12b_send_random.ino
├── rfm12b_thingspeak/
│ ├── README.md
│ └── rfm12b_thingspeak.ino
├── rp2040_dac7578/
│ ├── blink_led.cpp
│ ├── blink_led.h
│ └── rp2040_dac7578.ino
├── teensy_cvgate_mcp4725_neopixel/
│ ├── colormap.h
│ └── teensy_cvgate_mcp4725_neopixel.ino
├── teensy_gps_temp_ttn/
│ ├── payload.h
│ ├── sensor.cpp
│ └── teensy_gps_temp_ttn.ino
├── teensy_midifilter/
│ ├── midiname.c
│ └── teensy_midifilter.ino
├── teensy_ttn1/
│ └── teensy_ttn1.ino
├── teensy_ttn2/
│ └── teensy_ttn2.ino
└── uno_dac7578/
├── blink_led.cpp
├── blink_led.h
└── uno_dac7578.ino
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.DS_Store
*.swp
secret.h
.vscode
================================================
FILE: .gitmodules
================================================
[submodule "esp8266_artnet_dmx512"]
path = esp8266_artnet_dmx512
url = git@github.com:robertoostenveld/esp8266_artnet_dmx512.git
================================================
FILE: PulseSensor_v0/AllSerialHandling.cpp
================================================
//////////
///////// All Serial Handling Code,
///////// It's Changeable with the 'serialVisual' variable
///////// Set it to 'true' or 'false' when it's declared at start of code.
/////////
void serialOutput(){ // Decide How To Output Serial.
if (serialVisual == true){
arduinoSerialMonitorVisual('-', Signal); // goes to function that makes Serial Monitor Visualizer
} else{
sendDataToSerial('S', Signal); // goes to sendDataToSerial function
}
}
// Decides How To OutPut BPM and IBI Data
void serialOutputWhenBeatHappens(){
if (serialVisual == true){ // Code to Make the Serial Monitor Visualizer Work
Serial.print("*** Heart-Beat Happened *** "); //ASCII Art Madness
Serial.print("BPM: ");
Serial.print(BPM);
Serial.print(" ");
} else{
sendDataToSerial('B',BPM); // send heart rate with a 'B' prefix
sendDataToSerial('Q',IBI); // send time between beats with a 'Q' prefix
}
}
// Sends Data to Pulse Sensor Processing App, Native Mac App, or Third-party Serial Readers.
void sendDataToSerial(char symbol, int data ){
Serial.print(symbol);
Serial.println(data);
}
// Code to Make the Serial Monitor Visualizer Work
void arduinoSerialMonitorVisual(char symbol, int data ){
const int sensorMin = 0; // sensor minimum, discovered through experiment
const int sensorMax = 1024; // sensor maximum, discovered through experiment
int sensorReading = data;
// map the sensor range to a range of 12 options:
int range = map(sensorReading, sensorMin, sensorMax, 0, 11);
// do something different depending on the
// range value:
switch (range) {
case 0:
Serial.println(""); /////ASCII Art Madness
break;
case 1:
Serial.println("---");
break;
case 2:
Serial.println("------");
break;
case 3:
Serial.println("---------");
break;
case 4:
Serial.println("------------");
break;
case 5:
Serial.println("--------------|-");
break;
case 6:
Serial.println("--------------|---");
break;
case 7:
Serial.println("--------------|-------");
break;
case 8:
Serial.println("--------------|----------");
break;
case 9:
Serial.println("--------------|----------------");
break;
case 10:
Serial.println("--------------|-------------------");
break;
case 11:
Serial.println("--------------|-----------------------");
break;
}
}
================================================
FILE: PulseSensor_v0/Interrupt.cpp
================================================
volatile int rate[10]; // array to hold last ten IBI values
volatile unsigned long sampleCounter = 0; // used to determine pulse timing
volatile unsigned long lastBeatTime = 0; // used to find IBI
volatile int P =512; // used to find peak in pulse wave, seeded
volatile int T = 512; // used to find trough in pulse wave, seeded
volatile int thresh = 525; // used to find instant moment of heart beat, seeded
volatile int amp = 100; // used to hold amplitude of pulse waveform, seeded
volatile boolean firstBeat = true; // used to seed rate array so we startup with reasonable BPM
volatile boolean secondBeat = false; // used to seed rate array so we startup with reasonable BPM
void interruptSetup(){
// Initializes Timer2 to throw an interrupt every 2mS.
TCCR2A = 0x02; // DISABLE PWM ON DIGITAL PINS 3 AND 11, AND GO INTO CTC MODE
TCCR2B = 0x06; // DON'T FORCE COMPARE, 256 PRESCALER
OCR2A = 0X7C; // SET THE TOP OF THE COUNT TO 124 FOR 500Hz SAMPLE RATE
TIMSK2 = 0x02; // ENABLE INTERRUPT ON MATCH BETWEEN TIMER2 AND OCR2A
sei(); // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
}
// THIS IS THE TIMER 2 INTERRUPT SERVICE ROUTINE.
// Timer 2 makes sure that we take a reading every 2 miliseconds
ISR(TIMER2_COMPA_vect){ // triggered when Timer2 counts to 124
cli(); // disable interrupts while we do this
Signal = analogRead(pulsePin); // read the Pulse Sensor
sampleCounter += 2; // keep track of the time in mS with this variable
int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise
// find the peak and trough of the pulse wave
if(Signal < thresh && N > (IBI/5)*3){ // avoid dichrotic noise by waiting 3/5 of last IBI
if (Signal < T){ // T is the trough
T = Signal; // keep track of lowest point in pulse wave
}
}
if(Signal > thresh && Signal > P){ // thresh condition helps avoid noise
P = Signal; // P is the peak
} // keep track of highest point in pulse wave
// NOW IT'S TIME TO LOOK FOR THE HEART BEAT
// signal surges up in value every time there is a pulse
if (N > 250){ // avoid high frequency noise
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ){
Pulse = true; // set the Pulse flag when we think there is a pulse
digitalWrite(blinkPin,HIGH); // turn on pin 13 LED
IBI = sampleCounter - lastBeatTime; // measure time between beats in mS
lastBeatTime = sampleCounter; // keep track of time for next pulse
if(secondBeat){ // if this is the second beat, if secondBeat == TRUE
secondBeat = false; // clear secondBeat flag
for(int i=0; i<=9; i++){ // seed the running total to get a realisitic BPM at startup
rate[i] = IBI;
}
}
if(firstBeat){ // if it's the first time we found a beat, if firstBeat == TRUE
firstBeat = false; // clear firstBeat flag
secondBeat = true; // set the second beat flag
sei(); // enable interrupts again
return; // IBI value is unreliable so discard it
}
// keep a running total of the last 10 IBI values
word runningTotal = 0; // clear the runningTotal variable
for(int i=0; i<=8; i++){ // shift data in the rate array
rate[i] = rate[i+1]; // and drop the oldest IBI value
runningTotal += rate[i]; // add up the 9 oldest IBI values
}
rate[9] = IBI; // add the latest IBI to the rate array
runningTotal += rate[9]; // add the latest IBI to runningTotal
runningTotal /= 10; // average the last 10 IBI values
BPM = 60000/runningTotal; // how many beats can fit into a minute? that's BPM!
QS = true; // set Quantified Self flag
// QS FLAG IS NOT CLEARED INSIDE THIS ISR
}
}
if (Signal < thresh && Pulse == true){ // when the values are going down, the beat is over
digitalWrite(blinkPin,LOW); // turn off pin 13 LED
Pulse = false; // reset the Pulse flag so we can do it again
amp = P - T; // get amplitude of the pulse wave
thresh = amp/2 + T; // set thresh at 50% of the amplitude
P = thresh; // reset these for next time
T = thresh;
}
if (N > 2500){ // if 2.5 seconds go by without a beat
thresh = 512; // set thresh default
P = 512; // set P default
T = 512; // set T default
lastBeatTime = sampleCounter; // bring the lastBeatTime up to date
firstBeat = true; // set these to avoid noise
secondBeat = false; // when we get the heartbeat back
}
sei(); // enable interrupts when youre done!
}// end isr
================================================
FILE: PulseSensor_v0/PulseSensor_v0.ino
================================================
/* Pulse Sensor Amped 1.4 by Joel Murphy and Yury Gitman http://www.pulsesensor.com
---------------------- Notes ---------------------- ----------------------
This code:
1) Blinks an LED to User's Live Heartbeat PIN 13
2) Fades an LED to User's Live HeartBeat
3) Determines BPM
4) Prints All of the Above to Serial
Read Me:
https://github.com/WorldFamousElectronics/PulseSensor_Amped_Arduino/blob/master/README.md
---------------------- ---------------------- ----------------------
*/
// Variables
int pulsePin = 0; // Pulse Sensor purple wire connected to analog pin 0
int blinkPin = 13; // pin to blink led at each beat
int fadePin = 5; // pin to do fancy classy fading blink at each beat
int fadeRate = 0; // used to fade LED on with PWM on fadePin
// Volatile Variables, used in the interrupt service routine!
volatile int BPM; // int that holds raw Analog in 0. updated every 2mS
volatile int Signal; // holds the incoming raw data
volatile int IBI = 600; // int that holds the time interval between beats! Must be seeded!
volatile boolean Pulse = false; // "True" when User's live heartbeat is detected. "False" when not a "live beat".
volatile boolean QS = false; // becomes true when Arduoino finds a beat.
// Regards Serial OutPut -- Set This Up to your needs
static boolean serialVisual = false; // Set to 'false' by Default. Re-set to 'true' to see Arduino Serial Monitor ASCII Visual Pulse
void setup(){
pinMode(blinkPin,OUTPUT); // pin that will blink to your heartbeat!
pinMode(fadePin,OUTPUT); // pin that will fade to your heartbeat!
Serial.begin(115200); // we agree to talk fast!
interruptSetup(); // sets up to read Pulse Sensor signal every 2mS
// IF YOU ARE POWERING The Pulse Sensor AT VOLTAGE LESS THAN THE BOARD VOLTAGE,
// UN-COMMENT THE NEXT LINE AND APPLY THAT VOLTAGE TO THE A-REF PIN
// analogReference(EXTERNAL);
}
// Where the Magic Happens
void loop(){
serialOutput() ;
if (QS == true){ // A Heartbeat Was Found
// BPM and IBI have been Determined
// Quantified Self "QS" true when arduino finds a heartbeat
fadeRate = 255; // Makes the LED Fade Effect Happen
// Set 'fadeRate' Variable to 255 to fade LED with pulse
serialOutputWhenBeatHappens(); // A Beat Happened, Output that to serial.
QS = false; // reset the Quantified Self flag for next time
}
ledFadeToBeat(); // Makes the LED Fade Effect Happen
delay(20); // take a break
}
void ledFadeToBeat(){
fadeRate -= 15; // set LED fade value
fadeRate = constrain(fadeRate,0,255); // keep LED fade value from going into negative numbers!
analogWrite(fadePin,fadeRate); // fade LED
}
================================================
FILE: PulseSensor_v0/README.md
================================================
# Arduino sketch for PulseSensor, see http://pulsesensor.com
This is the original code from https://github.com/WorldFamousElectronics/PulseSensor_Amped_Arduino. I have only renamed it for consistency with my other PulseSense sketches.
================================================
FILE: PulseSensor_v0/Timer_Interrupt_Notes.cpp
================================================
/*
These notes put together by Joel Murphy for Pulse Sensor Amped, 2015
The code that this section is attached to uses a timer interrupt
to sample the Pulse Sensor with consistent and regular timing.
The code is setup to read Pulse Sensor signal at 500Hz (every 2mS).
The reasoning for this can be found here:
http://pulsesensor.com/pages/pulse-sensor-amped-arduino-v1dot1
There are issues with using different timers to control the Pulse Sensor sample rate.
Sometimes, user will need to switch timers for access to other code libraries.
Also, some other hardware may have different timer setup requirements. This page
will cover those different needs and reveal the necessary settings. There are two
part of the code that will be discussed. The interruptSetup() routine, and
the interrupt function call. Depending on your needs, or the Arduino variant that you use,
check below for the correct settings.
******************************************************************************************
ARDUINO UNO, Pro 328-5V/16MHZ, Pro-Mini 328-5V/16MHz (or any board with ATmega328P running at 16MHz)
>> Timer2
Pulse Sensor Arduino UNO uses Timer2 by default.
Use of Timer2 interferes with PWM on pins 3 and 11.
There is also a conflict with the Tone library, so if you want tones, use Timer1 below.
void interruptSetup(){
// Initializes Timer2 to throw an interrupt every 2mS.
TCCR2A = 0x02; // DISABLE PWM ON DIGITAL PINS 3 AND 11, AND GO INTO CTC MODE
TCCR2B = 0x06; // DON'T FORCE COMPARE, 256 PRESCALER
OCR2A = 0X7C; // SET THE TOP OF THE COUNT TO 124 FOR 500Hz SAMPLE RATE
TIMSK2 = 0x02; // ENABLE INTERRUPT ON MATCH BETWEEN TIMER2 AND OCR2A
sei(); // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
}
use the following interrupt vector with Timer2
ISR(TIMER2_COMPA_vect)
>> Timer1
Use of Timer1 interferes with PWM on pins 9 and 10.
The Servo library also uses Timer1, so if you want servos, use Timer2 above.
void interruptSetup(){
// Initializes Timer1 to throw an interrupt every 2mS.
TCCR1A = 0x00; // DISABLE OUTPUTS AND PWM ON DIGITAL PINS 9 & 10
TCCR1B = 0x11; // GO INTO 'PHASE AND FREQUENCY CORRECT' MODE, NO PRESCALER
TCCR1C = 0x00; // DON'T FORCE COMPARE
TIMSK1 = 0x01; // ENABLE OVERFLOW INTERRUPT (TOIE1)
ICR1 = 16000; // TRIGGER TIMER INTERRUPT EVERY 2mS
sei(); // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
}
Use the following ISR vector for the Timer1 setup above
ISR(TIMER1_OVF_vect)
>> Timer0
DON'T USE TIMER0! Timer0 is used for counting delay(), millis(), and micros().
Messing with Timer0 is highly unadvised!
******************************************************************************************
ARDUINO Fio, Lilypad, ProMini328-3V/8MHz (or any board with ATmega328P running at 8MHz)
>> Timer2
Pulse Sensor Arduino UNO uses Timer2 by default.
Use of Timer2 interferes with PWM on pins 3 and 11.
There is also a conflict with the Tone library, so if you want tones, use Timer1 below.
void interruptSetup(){
// Initializes Timer2 to throw an interrupt every 2mS.
TCCR2A = 0x02; // DISABLE PWM ON DIGITAL PINS 3 AND 11, AND GO INTO CTC MODE
TCCR2B = 0x05; // DON'T FORCE COMPARE, 128 PRESCALER
OCR2A = 0X7C; // SET THE TOP OF THE COUNT TO 124 FOR 500Hz SAMPLE RATE
TIMSK2 = 0x02; // ENABLE INTERRUPT ON MATCH BETWEEN TIMER2 AND OCR2A
sei(); // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
}
use the following interrupt vector with Timer2
ISR(TIMER2_COMPA_vect)
>> Timer1
Use of Timer1 interferes with PWM on pins 9 and 10.
The Servo library also uses Timer1, so if you want servos, use Timer2 above.
void interruptSetup(){
// Initializes Timer1 to throw an interrupt every 2mS.
TCCR1A = 0x00; // DISABLE OUTPUTS AND PWM ON DIGITAL PINS 9 & 10
TCCR1B = 0x11; // GO INTO 'PHASE AND FREQUENCY CORRECT' MODE, NO PRESCALER
TCCR1C = 0x00; // DON'T FORCE COMPARE
TIMSK1 = 0x01; // ENABLE OVERFLOW INTERRUPT (TOIE1)
ICR1 = 8000; // TRIGGER TIMER INTERRUPT EVERY 2mS
sei(); // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
}
Use the following ISR vector for the Timer1 setup above
ISR(TIMER1_OVF_vect)
>> Timer0
DON'T USE TIMER0! Timer0 is used for counting delay(), millis(), and micros().
Messing with Timer0 is highly unadvised!
******************************************************************************************
ARDUINO Leonardo (or any board with ATmega32u4 running at 16MHz)
>> Timer1
Use of Timer1 interferes with PWM on pins 9 and 10.
void interruptSetup(){
TCCR1A = 0x00;
TCCR1B = 0x0C; // prescaler = 256
OCR1A = 0x7C; // count to 124
TIMSK1 = 0x02;
sei();
}
The only other thing you will need is the correct ISR vector in the next step.
ISR(TIMER1_COMPA_vect)
******************************************************************************************
ADAFRUIT Flora, ARDUINO Fio v3 (or any other board with ATmega32u4 running at 8MHz)
>> Timer1
Use of Timer1 interferes with PWM on pins 9 and 10.
void interruptSetup(){
TCCR1A = 0x00;
TCCR1B = 0x0C; // prescaler = 256
OCR1A = 0x3E; // count to 62
TIMSK1 = 0x02;
sei();
}
The only other thing you will need is the correct ISR vector in the next step.
ISR(TIMER1_COMPA_vect)
******************************************************************************************
ADAFRUIT Gemma (or any other board with ATtiny85 running at 8MHz)
NOTE: Gemma does not do serial communication!
Comment out or remove the Serial code in the Arduino sketch!
Timer1
Use of Timer1 breaks PWM output on pin D1
void interruptSetup(){
TCCR1 = 0x88; // Clear Timer on Compare, Set Prescaler to 128 TEST VALUE
GTCCR &= 0x81; // Disable PWM, don't connect pins to events
OCR1C = 0x7C; // Set the top of the count to 124 TEST VALUE
OCR1A = 0x7C; // Set the timer to interrupt after counting to TEST VALUE
bitSet(TIMSK,6); // Enable interrupt on match between TCNT1 and OCR1A
sei(); // Enable global interrupts
}
The only other thing you will need is the correct ISR vector in the next step.
ISR(TIMER1_COMPA_vect)
******************************************************************************************
******************************************************************************************
******************************************************************************************
******************************************************************************************
******************************************************************************************
******************************************************************************************
******************************************************************************************
*/
================================================
FILE: PulseSensor_v1/PulseSensor_v1.ino
================================================
// PulseSensor application that streams continuous data at 500Hz using the OpenEEG data format
// over the serial port or a BlueTooth connection
#define LSB(x) (byte)(x & 0xff);
#define MSB(x) (byte)(x>>8 & 0xff);
#define hasUSB
// #define hasBT
unsigned int value = 0, sample = 0;
unsigned long tsample = 4; // should be 4 to approximate 256 Hz according to protocol
byte buf[17];
void setup() {
pinMode(A0, INPUT); // connected to PulseSensor analog output
#ifdef hasUSB
Serial.begin(57600); // connected to USB
unsigned long tic = millis();
while ((millis() - tic) < 1000 && !(Serial)); // max 1 second
#endif
#ifdef hasBT
Serial1.begin(57600); // connected to BlueSMiRF
unsigned long tic = millis();
while ((millis() - tic) < 1000 && !(Serial1)); // max 1 second
#endif
// the OpenEEG protocol sends data in 17 byte packets
buf[ 0] = 0xA5;
buf[ 1] = 0x5A;
buf[ 2] = 2; // version
buf[ 3] = 0; // count
buf[ 4] = 0; // channel 1
buf[ 5] = 0;
buf[ 6] = 0; // channel 2
buf[ 7] = 0;
buf[ 8] = 0; // channel 3
buf[ 9] = 0;
buf[10] = 0; // channel 4
buf[11] = 0;
buf[12] = 0; // channel 5
buf[13] = 0;
buf[14] = 0; // channel 6
buf[15] = 0;
buf[16] = 0; // switches
}
void loop() {
// read the analog value
value = analogRead(A0);
// scale to appropriate values
value *= 32;
// increment the sample number
sample++;
// update the data packet
buf[ 3]++;
buf[ 4] = MSB(value); // channel 1, MSB
buf[ 5] = LSB(value); // channel 1, LSB
buf[ 6] = MSB(sample); // channel 2, MSB
buf[ 7] = LSB(sample); // channel 2, LSB
#ifdef hasUSB
Serial.flush();
for (int i = 0; i < 17; i++) {
Serial.write(buf[i]);
}
#endif
#ifdef hasBT
Serial1.flush();
for (int i = 0; i < 17; i++) {
Serial1.write(buf[i]);
}
#endif
delay(tsample);
} // loop
================================================
FILE: PulseSensor_v1/README.md
================================================
# Arduino sketch for PulseSensor, see http://pulsesensor.com
This reads the analog value of the sensor and sends it over the serial port in OpenEEG format. This allows the data to be visualized in [any of these software packages](http://openeeg.sourceforge.net/doc/sw/) or in realtime EGE processing software such as [BCI2000](http://www.bci2000.org) or [FieldTrip](http://www.fieldtriptoolbox.org/development/realtime/modulareeg).
The sample timing is relatively inaccurate. It assumes that the code to read the analog value and to write serial output takes negligible time. At the end of each loop there is a delay of 4ms, resulting in approximately 250 Hz sampling rate.
================================================
FILE: PulseSensor_v2/Interrupt.cpp
================================================
volatile int rate[10]; // array to hold last ten IBI values
volatile unsigned long lastBeatTime = 0; // used to find IBI
volatile int P = 512; // used to find peak in pulse wave, seeded
volatile int T = 512; // used to find trough in pulse wave, seeded
volatile int thresh = 525; // used to find instant moment of heart beat, seeded
volatile int amp = 100; // used to hold amplitude of pulse waveform, seeded
volatile boolean firstBeat = true; // used to seed rate array so we startup with reasonable BPM
volatile boolean secondBeat = false; // used to seed rate array so we startup with reasonable BPM
void interruptSetup() {
// Initializes Timer2 to throw an interrupt every 2mS.
TCCR2A = 0x02; // DISABLE PWM ON DIGITAL PINS 3 AND 11, AND GO INTO CTC MODE
TCCR2B = 0x06; // DON'T FORCE COMPARE, 256 PRESCALER
OCR2A = 0X7C; // SET THE TOP OF THE COUNT TO 124 FOR 500Hz SAMPLE RATE
TIMSK2 = 0x02; // ENABLE INTERRUPT ON MATCH BETWEEN TIMER2 AND OCR2A
sei(); // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
}
// THIS IS THE TIMER 2 INTERRUPT SERVICE ROUTINE.
// Timer 2 makes sure that we take a reading every 2 miliseconds
ISR(TIMER2_COMPA_vect) { // triggered when Timer2 counts to 124
cli(); // disable interrupts while we do this
Signal = analogRead(pulsePin); // read the Pulse Sensor
sampleCounter += 2; // keep track of the time in mS with this variable
int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise
// find the peak and trough of the pulse wave
if (Signal < thresh && N > (IBI / 5) * 3) { // avoid dichrotic noise by waiting 3/5 of last IBI
if (Signal < T) { // T is the trough
T = Signal; // keep track of lowest point in pulse wave
}
}
if (Signal > thresh && Signal > P) { // thresh condition helps avoid noise
P = Signal; // P is the peak
} // keep track of highest point in pulse wave
// NOW IT'S TIME TO LOOK FOR THE HEART BEAT
// signal surges up in value every time there is a pulse
if (N > 250) { // avoid high frequency noise
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI / 5) * 3) ) {
Pulse = true; // set the Pulse flag when we think there is a pulse
digitalWrite(blinkPin, HIGH); // turn on pin 13 LED
IBI = sampleCounter - lastBeatTime; // measure time between beats in mS
lastBeatTime = sampleCounter; // keep track of time for next pulse
if (secondBeat) { // if this is the second beat, if secondBeat == TRUE
secondBeat = false; // clear secondBeat flag
for (int i = 0; i <= 9; i++) { // seed the running total to get a realisitic BPM at startup
rate[i] = IBI;
}
}
if (firstBeat) { // if it's the first time we found a beat, if firstBeat == TRUE
firstBeat = false; // clear firstBeat flag
secondBeat = true; // set the second beat flag
sei(); // enable interrupts again
return; // IBI value is unreliable so discard it
}
// keep a running total of the last 10 IBI values
word runningTotal = 0; // clear the runningTotal variable
for (int i = 0; i <= 8; i++) { // shift data in the rate array
rate[i] = rate[i + 1]; // and drop the oldest IBI value
runningTotal += rate[i]; // add up the 9 oldest IBI values
}
rate[9] = IBI; // add the latest IBI to the rate array
runningTotal += rate[9]; // add the latest IBI to runningTotal
runningTotal /= 10; // average the last 10 IBI values
BPM = 60000 / runningTotal; // how many beats can fit into a minute? that's BPM!
QS = true; // set Quantified Self flag
// QS FLAG IS NOT CLEARED INSIDE THIS ISR
}
}
if (Signal < thresh && Pulse == true) { // when the values are going down, the beat is over
digitalWrite(blinkPin, LOW); // turn off pin 13 LED
Pulse = false; // reset the Pulse flag so we can do it again
amp = P - T; // get amplitude of the pulse wave
thresh = amp / 2 + T; // set thresh at 50% of the amplitude
P = thresh; // reset these for next time
T = thresh;
}
if (N > 2500) { // if 2.5 seconds go by without a beat
thresh = 512; // set thresh default
P = 512; // set P default
T = 512; // set T default
lastBeatTime = sampleCounter; // bring the lastBeatTime up to date
firstBeat = true; // set these to avoid noise
secondBeat = false; // when we get the heartbeat back
}
sei(); // enable interrupts when youre done!
}// end isr
================================================
FILE: PulseSensor_v2/PulseSensor_v2.ino
================================================
#define LSB(x) (byte)(x & 0xff);
#define MSB(x) (byte)(x>>8 & 0xff);
#define pulsePin A0 // Pulse Sensor purple wire connected to analog pin 0
#define blinkPin 13 // pin to blink led at each beat
// Variables
int TTL = 0;
unsigned long ledTime = 0, flushTime = 0;
unsigned long prevSample = 0;
byte buf[17];
// Volatile Variables, used in the interrupt service routine!
volatile unsigned long sampleCounter = 0; // used to determine pulse timing
volatile int Signal; // int that holds raw Analog in 0. This is updated every 2mS
volatile int BPM; // int that holds the beats per minute
volatile int IBI = 600; // int that holds the inter beat interval. Must be seeded!
volatile boolean Pulse = false; // "True" when User's live heartbeat is detected. "False" when not a "live beat".
volatile boolean QS = false; // becomes true when Arduino finds a beat.
void setup() {
pinMode(pulsePin, INPUT); // connected to PulseSensor analog output
pinMode(blinkPin, OUTPUT); // pin that will blink to your heartbeat!
Serial.begin(57600);
unsigned long tic = millis();
while ((millis() - tic) < 1000 && !(Serial)); // max 1 second
interruptSetup(); // sets up to read Pulse Sensor signal every 2mS
// analogReference(EXTERNAL);
// the OpenEEG protocol sends data in 17 byte packets
buf[ 0] = 0xA5;
buf[ 1] = 0x5A;
buf[ 2] = 2; // version
buf[ 3] = 0; // count
buf[ 4] = 0; // channel 1
buf[ 5] = 0;
buf[ 6] = 0; // channel 2
buf[ 7] = 0;
buf[ 8] = 0; // channel 3
buf[ 9] = 0;
buf[10] = 0; // channel 4
buf[11] = 0;
buf[12] = 0; // channel 5
buf[13] = 0;
buf[14] = 0; // channel 6
buf[15] = 0;
buf[16] = 0; // switches
}
void loop() {
// the processing by the interrupt handler is done every 2 ms, i.e. at 500Hz
// the OpenEEG data format expects sampling to be at 256 Hz, so we write every 4 ms
if ((sampleCounter > prevSample) && (sampleCounter % 4) == 0) {
buf[ 3]++;
buf[ 4] = MSB(Signal*32);
buf[ 5] = LSB(Signal*32);
buf[ 6] = MSB(TTL);
buf[ 7] = LSB(TTL);
buf[ 8] = MSB(BPM);
buf[ 9] = LSB(BPM);
buf[10] = MSB(IBI);
buf[11] = LSB(IBI);
buf[12] = MSB(sampleCounter);
buf[13] = LSB(sampleCounter);
prevSample = sampleCounter;
// Write the packet to the serial port
for (int i = 0; i < 17; i++) {
Serial.write(buf[i]);
}
}
if ((millis() - flushTime) > 100) {
// Flush the serial port now and then
Serial.flush();
flushTime = millis();
}
if ((millis() - ledTime) > 100) {
// Switch off the LED and the TTL signal
analogWrite(blinkPin, 0);
TTL = 0;
}
if (QS == true) {
// Switch on the LED and the TTL signal
ledTime = millis();
analogWrite(blinkPin, 255);
TTL = 512;
QS = false;
}
// All timing critical code is implemented by means of an interrupt
// Take a short break in the main loop
delay(2);
}
================================================
FILE: PulseSensor_v2/README.md
================================================
# Arduino sketch for PulseSensor, see http://pulsesensor.com
This code is derived from the [original code](https://github.com/WorldFamousElectronics/PulseSensor_Amped_Arduino). It uses the same interrupt driven sampling and processing of the data, but the more fancy features have been removed (ASCII art serial output, fading led). It outputs the data at 250Hz over the serial connection in OpenEEG format with the following channels.
1. contains the continuously sampled value
2. contains a TTL-level representation of heart beat events
3. contains the beats per minute
4. contains the inter beat interval in milliseconds
5. contains the sample time in milliseconds
================================================
FILE: README.md
================================================
# Arduino electronics hacking
Most of the code here corresponds to electronics hardware that is documented on my [personal homepage](http://robertoostenveld.nl/category/arduino/).
The code that I wrote and/or collected in this repository is mostly a mash-up of Arduino code snippets and examples that I found at various places on the internet. There are pieces where I am not even sure if I could be considered the original author. For the pieces of code here in which I invested more substantial (creative) efforts, I am happy to share back to the community without restrictions. So unless otherwise stated, please consider the code here to be in the public domain, i.e. you can do with it what you want. See https://fairuse.stanford.edu/overview/public-domain/welcome/
Please let me know if you see code here for which you consider yourself to be the creator and for which I have not properly credited you, for example through an issue or pull request.
Note that the code for the ESP8266-based ArtNet to DMX512 module that is documented [here](https://robertoostenveld.nl/art-net-to-dmx512-with-esp8266/) is not part of this repository any more but has moved to a stand-alone [esp8266_artnet_dmx512](https://github.com/robertoostenveld/esp8266_artnet_dmx512) repository.
================================================
FILE: bitsi/bitsi.ino
================================================
/*
* BITSI - Bits to Serial Interface
*
* This is an Arduino Uno based serial/usb interface that can be used to monitor responses from
* button boxes and to send TTL-level stimulus triggers to the parallel input port of EEG or MEG
* acquisition systems.
*
* This code originates from https://code.google.com/archive/p/dccn-lab/ where it was released under the GNU GPL v2
*
* Copyright (C) 2009-2010 Erik van der Boogert & Bram Daams (for the original implementation)
* Copyright (C) 2025-2026 Robert Oostenveld (for this cleaned up version)
*
* From version v94, the Bitsi trigger pulse length can be programmed. In theory, the pulse length can be
* between 1 and 255 ms. Therefore we send three codes in sequence to the Bitsi: [0], [1], [pulselength].
* By sending [0], we put the Bitsi in programming mode. Sending [1] tells the Bitsi we want to change
* the pulse length, and the [pulselength] will be the value we want the trigger pulse to last.
*
* Version v94 also added the level mode to the functionality of the Bitsi. This means that when you
* trigger an output, the output will remain 'high', until you send another code or reset the output.
* The level mode functionality is NOT SUPPORTED by this specific implementation of the Bitsi firmware.
*
*/
// Constants
const unsigned long TIMEOUT_DEBOUNCE = 8; // Debounce interval in ms
const int PIN_BUILTIN_LED = 13;
// Input pins configuration
const int INPUT_PINS[8] = {10, 11, 12, 14, 15, 16, 17, 18};
// Output pins configuration
const int OUTPUT_PINS[8] = {2, 3, 4, 5, 6, 7, 8, 9};
unsigned long TIMEOUT_PULSE_LENGTH = 15; // Pulse length in ms
// Variables
byte serialInChar; // To receive desired Parallel_Out value
unsigned long timeNow = 0; // Current time reference
unsigned long timeOnsetOut = 0; // When Parallel_Out was set
bool isParallelOutActive = false; // Whether output pulse is active
// Input state tracking
bool buttonState[8] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};
bool prevInputState[8] = {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH};
// Debounce tracking
unsigned long debounceStartTime[8] = {0, 0, 0, 0, 0, 0, 0, 0};
bool isDebouncing[8] = {false, false, false, false, false, false, false, false};
// For initialization of the duration and/or levelmode programming
typedef enum operatingMode {
NORMAL,
PROGRAMMING,
PULSELENGTH,
LEVELMODE
} operatingMode_t;
operatingMode_t operatingMode;
void setup() {
// Initialize serial port
Serial.begin(115200);
// Set the initial operating mode
operatingMode = NORMAL;
// Configure ports
setupInputPort();
setupOutputPort();
writeOutputPort(0);
// Configure built-in LED
pinMode(PIN_BUILTIN_LED, OUTPUT);
digitalWrite(PIN_BUILTIN_LED, LOW);
// Startup LED sequence
for (byte i = 0; i < 8; i++) {
writeOutputPort((B00000001 << (i + 1)) - 1);
digitalWrite(PIN_BUILTIN_LED, !digitalRead(PIN_BUILTIN_LED));
delay(100);
}
writeOutputPort(0);
delay(100);
// Show ready message
Serial.println("BITSI mode, Ready!");
}
void loop() {
// Get current time reference
timeNow = millis();
// Process serial input
processSerialInput();
// Process button pins
processButtonPins();
// Handle debounce timeouts
handleDebounceTimeouts();
}
void processSerialInput() {
if (Serial.available()) {
serialInChar = Serial.read();
// deal with the initialization of the pulse length and/or levelmode programming
if (operatingMode == NORMAL && serialInChar == 0) {
operatingMode = PROGRAMMING;
}
else if (operatingMode == PROGRAMMING && serialInChar == 1) {
operatingMode = PULSELENGTH;
}
else if (operatingMode == PROGRAMMING && serialInChar == 2) {
operatingMode = LEVELMODE;
}
else if (operatingMode == PULSELENGTH) {
// the third byte is the pulse duration
TIMEOUT_PULSE_LENGTH = serialInChar;
operatingMode = NORMAL;
}
else if (operatingMode == LEVELMODE) {
// the third byte would be the bitmask, but level mode is not supported here
operatingMode = NORMAL;
}
else {
// output the incoming character as TTL signals
writeOutputPort(serialInChar);
timeOnsetOut = timeNow;
isParallelOutActive = true;
}
}
else if (isParallelOutActive && ((timeNow - timeOnsetOut) > TIMEOUT_PULSE_LENGTH)) {
writeOutputPort(0);
isParallelOutActive = false;
}
}
void processButtonPins() {
for (byte i = 0; i < 8; i++) {
buttonState[i] = digitalRead(INPUT_PINS[i]);
if ((prevInputState[i] != buttonState[i]) && !isDebouncing[i]) {
// Send corresponding character (A-H for high, a-h for low)
Serial.print(char(buttonState[i] == HIGH ? (65 + i) : (97 + i)));
// Start debouncing
debounceStartTime[i] = millis();
isDebouncing[i] = true;
prevInputState[i] = buttonState[i];
}
}
}
void handleDebounceTimeouts() {
for (byte i = 0; i < 8; i++) {
if (isDebouncing[i] && ((timeNow - debounceStartTime[i]) > TIMEOUT_DEBOUNCE)) {
isDebouncing[i] = false;
}
}
}
void setupOutputPort() {
for (int pin = 0; pin <8; pin++) {
pinMode(OUTPUT_PINS[pin], OUTPUT);
digitalWrite(OUTPUT_PINS[pin], LOW); // Initialize outputs to LOW
}
}
void setupInputPort() {
for (int i = 0; i < 8; i++) {
pinMode(INPUT_PINS[i], INPUT);
digitalWrite(INPUT_PINS[i], HIGH); // Enable pull-up resistors
}
}
byte readInputPort() {
byte portB = PINB;
byte portC = PINC;
return ((portB & B00011100) >> 2) | ((portC & B00011111) << 3);
}
byte readOutputPort() {
byte portC = PINC;
byte portD = PIND;
return ((portD & B11111100) >> 2) | ((portC & B00000011) << 6);
}
void writeOutputPort(byte code) {
// Calculate new port states
byte portB = (code & B11000000) >> 6 | (PINB & B11111100);
byte portD = (code & B00111111) << 2;
// Update ports
PORTB = portB;
PORTD = portD;
}
================================================
FILE: blenderdefender/blenderdefender.ino
================================================
#include <JeeLib.h>
#include <Ports.h>
#define pirPin 9 // the number of the pushbutton pin
#define redLed 5 // the number of the LED pin
#define buttonPin 4 // the number of the pushbutton pin
#define greenLed 3 // the number of the LED pin
//uncomment this line for debugging through the serial interface
// #define BLENDER_DEBUG
#define KAKU_GROUP 1
#define KAKU_ADDR 1
// this is used to save some energy while sleeping
ISR(WDT_vect) {
Sleepy::watchdogEvent();
}
int active = LOW;
int armed = LOW;
int engaged = LOW;
int buttonState; // the current value from the input pin
int pirState; // the current value from the input pin
int lastButtonState = HIGH; // the previous value from the input pin
int lastPirState = LOW; // the previous value from the input pin
int lastSwitchState = -1; // the last known state of the kaku switch
long buttonDebounceTime = 0; // the last time the button pin was toggled
long pirDebounceTime = 0; // the last time the PIR pin was toggled
long lastSwitchTime = 0;
#define debounceDelay 5 // the debounce time; increase if the output flickers
#define armedDelay 5000
#define blinkTime 500
/****************************************************************************************/
void setup() {
#ifdef BLENDER_DEBUG
Serial.begin(57600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
Serial.print("\n[blenderdefender / ");
Serial.print(__DATE__);
Serial.print(" / ");
Serial.print(__TIME__);
Serial.println("]");
#endif
// these should start in the expected value at rest
digitalWrite(buttonPin, HIGH);
digitalWrite(pirPin, LOW);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(pirPin, INPUT);
pinMode(redLed, OUTPUT);
pinMode(greenLed, OUTPUT);
// start with the switch off
kakuSwitch(0);
// set initial LED state
ledStatus();
}
/****************************************************************************************/
void ledStatus() {
#ifdef BLENDER_DEBUG
Serial.print("active = ");
Serial.print(active);
Serial.print("\tarmed = ");
Serial.print(armed);
Serial.print("\tengaged = ");
Serial.println(engaged);
delay(10);
#endif
if (engaged==HIGH) {
digitalWrite(redLed, HIGH);
digitalWrite(greenLed, HIGH);
}
else if (active==LOW) {
digitalWrite(redLed, LOW);
digitalWrite(greenLed, HIGH);
}
else if (active==HIGH && armed==LOW) {
digitalWrite(redLed, (int)((millis()-buttonDebounceTime)/blinkTime) % 2);
digitalWrite(greenLed, LOW);
}
else if (active==HIGH && armed==HIGH) {
digitalWrite(redLed, HIGH);
digitalWrite(greenLed, LOW);
}
} // ledStatus
/****************************************************************************************/
void loop() {
int buttonValue = digitalRead(buttonPin);
int pirValue = digitalRead(pirPin);
if (buttonValue != lastButtonState)
buttonDebounceTime = millis();
if (pirValue != lastPirState)
pirDebounceTime = millis();
if ((millis() - buttonDebounceTime) > debounceDelay) {
if (buttonValue != buttonState) {
buttonState = buttonValue;
#ifdef BLENDER_DEBUG
Serial.print("buttonState = ");
Serial.println(buttonState);
#endif
if (buttonState == LOW) {
active = !active;
armed = LOW;
engaged = LOW;
}
}
}
if (active && (millis() - buttonDebounceTime) > armedDelay)
armed = HIGH;
if ((millis() - pirDebounceTime) > debounceDelay) {
if (pirValue != pirState) {
#ifdef BLENDER_DEBUG
Serial.print("pirState = ");
Serial.println(pirState);
#endif
pirState = pirValue;
if ((pirState == HIGH) && active && armed) {
engaged = HIGH;
}
else {
engaged = LOW;
}
}
}
// update the LEDs
ledStatus();
if (active && armed && !engaged && (millis() - lastSwitchTime) > 60000) {
// switch the blender off every minute to safe-guard that it remains off most of the time
lastSwitchTime = millis();
kakuSwitch(0);
}
if (lastSwitchState!=engaged) {
// toggle the blender on or off
kakuSwitch(engaged);
lastSwitchState = engaged;
lastSwitchTime = millis();
}
// remember these for the next iteration of the loop
lastButtonState = buttonValue;
lastPirState = pirValue;
// spend some time in sleep mode to save energy
Sleepy::loseSomeTime(20);
} // loop
/****************************************************************************************/
void kakuSwitch(int engaged) {
#ifdef BLENDER_DEBUG
Serial.print("kakuSwitch ");
#endif
rf12_initialize(0, RF12_433MHZ, 0);
#ifdef BLENDER_DEBUG
Serial.println(engaged);
#endif
// send it twice to improve the robustness
kakuSend(KAKU_GROUP, KAKU_ADDR, engaged);
kakuSend(KAKU_GROUP, KAKU_ADDR, engaged);
}
/****************************************************************************************/
// this is from RF12demo
static void kakuSend(char addr, byte device, byte on) {
int cmd = 0x600 | ((device - 1) << 4) | ((addr - 1) & 0xF);
if (on)
cmd |= 0x800;
for (byte i = 0; i < 4; ++i) {
for (byte bit = 0; bit < 12; ++bit) {
ookPulse(375, 1125);
int on = bitRead(cmd, bit) ? 1125 : 375;
ookPulse(on, 1500 - on);
}
ookPulse(375, 375);
delay(11); // approximate
}
}
/****************************************************************************************/
// this is from RF12demo
static void ookPulse(int on, int off) {
rf12_onOff(1);
delayMicroseconds(on + 150);
rf12_onOff(0);
delayMicroseconds(off - 200);
}
================================================
FILE: digispark_skateboard/README.md
================================================
# Digispark Skateboard
This is an Arduino sketch to implement a strip of WS2812b Neopixels
under a skateboard. It features multiple blinking and moving patterns
of light, which can be selected using three latching pushbuttons,
allowing for in total 8 modes.
It is implemneted using a
[Digispark](https://www.kickstarter.com/projects/digistump/digispark-the-tiny-arduino-enabled-usb-dev-board)
USB development board and two 50 cm long LED strips, each with 30
LEDS. The LED strips are directly powered from a 3.7-4.2 V 18650
LiPo battery, the Digispark is powered using a small DC-DC transformer
module.
## Light modes
One button is used to switch the power on/off, the other three
buttons allow selecting between up to 8 different light modes. The
implementation of the light modes is borrowed from one of my other
projects: which are [ESP8266 powered ArtNet neopixel
modules](https://robertoostenveld.nl/esp-8266-art-net-neopixel-module/).
- static single uniform color
- uniform blinking between two colors
- rotating color wheel with another color background
- rotating HSV rainbow
## Components
- Digispark rev4
- 18650 LiPo battery with builit-in protection
- battery holder
- USB DC-DC transformer with 5V output
- 4 latching switches, 1x for power, 3x for mode selection
- 3 pull-up resistors, few kOhm
- capacitor, 1000uF
- 3-D printed enclosure
================================================
FILE: digispark_skateboard/colorspace.cpp
================================================
#include "colorspace.h"
hsv rgb2hsv(rgb in)
{
hsv out;
double min, max, delta;
min = in.r < in.g ? in.r : in.g;
min = min < in.b ? min : in.b;
max = in.r > in.g ? in.r : in.g;
max = max > in.b ? max : in.b;
out.v = max; // v
delta = max - min;
if (delta < 0.00001)
{
out.s = 0;
out.h = 0; // undefined, maybe nan?
return out;
}
if ( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash
out.s = (delta / max); // s
} else {
// if max is 0, then r = g = b = 0
// s = 0, v is undefined
out.s = 0.0;
out.h = 0. / 0.; // its now undefined
return out;
}
if ( in.r >= max ) // > is bogus, just keeps compilor happy
out.h = ( in.g - in.b ) / delta; // between yellow & magenta
else if ( in.g >= max )
out.h = 2.0 + ( in.b - in.r ) / delta; // between cyan & yellow
else
out.h = 4.0 + ( in.r - in.g ) / delta; // between magenta & cyan
out.h *= 60.0; // degrees
if ( out.h < 0.0 )
out.h += 360.0;
return out;
}
rgb hsv2rgb(hsv in)
{
double hh, p, q, t, ff;
long i;
rgb out;
if (in.s <= 0.0) { // < is bogus, just shuts up warnings
out.r = in.v;
out.g = in.v;
out.b = in.v;
return out;
}
hh = in.h;
if (hh >= 360.0) hh = 0.0;
hh /= 60.0;
i = (long)hh;
ff = hh - i;
p = in.v * (1.0 - in.s);
q = in.v * (1.0 - (in.s * ff));
t = in.v * (1.0 - (in.s * (1.0 - ff)));
switch (i) {
case 0:
out.r = in.v;
out.g = t;
out.b = p;
break;
case 1:
out.r = q;
out.g = in.v;
out.b = p;
break;
case 2:
out.r = p;
out.g = in.v;
out.b = t;
break;
case 3:
out.r = p;
out.g = q;
out.b = in.v;
break;
case 4:
out.r = t;
out.g = p;
out.b = in.v;
break;
case 5:
default:
out.r = in.v;
out.g = p;
out.b = q;
break;
}
return out;
}
================================================
FILE: digispark_skateboard/colorspace.h
================================================
#ifndef _COLORSPACE_H_
#define _COLORSPACE_H_
typedef struct {
double r; // percent
double g; // percent
double b; // percent
} rgb;
typedef struct {
double h; // angle in degrees
double s; // percent
double v; // percent
} hsv;
hsv rgb2hsv(rgb);
rgb hsv2rgb(hsv);
#endif
================================================
FILE: digispark_skateboard/digispark_skateboard.ino
================================================
#include <Arduino.h>
#include "neopixel_mode.h"
// the neopixel strip is connected to pin 0
#define BUILTIN_LED 1
#define BUTTON0 2
#define BUTTON1 3
#define BUTTON2 4
#define BRIGHTNESS 128 // can be up to 255
// mode1 details are specified as r, g, b
// mode4 details are specified as r1, g1, b1, r2, g2, b2, speed
// mode10 details are specified as r1, g1, b1, r2, g2, b2, speed
// mode12 details are specified as saturation, value, speed
uint8_t specification0[] = {BRIGHTNESS, BRIGHTNESS, BRIGHTNESS}; // mode1
uint8_t specification1[] = {BRIGHTNESS, 0, 0, 0, BRIGHTNESS, 0, 2}; // mode4
uint8_t specification2[] = {0, BRIGHTNESS, 0, 0, 0, BRIGHTNESS, 2}; // mode4
uint8_t specification3[] = {0, 0, BRIGHTNESS, BRIGHTNESS, 0, 0, 2}; // mode4
uint8_t specification4[] = {BRIGHTNESS, 0, 0, 0, BRIGHTNESS, 0, 2}; // mode10
uint8_t specification5[] = {0, BRIGHTNESS, 0, 0, 0, BRIGHTNESS, 2}; // mode10
uint8_t specification6[] = {0, 0, BRIGHTNESS, BRIGHTNESS, 0, 0, 2}; // mode10
uint8_t specification7[] = {220, BRIGHTNESS, 1}; // mode12
unsigned long previous = 0, now;
void setup() {
pinMode(BUILTIN_LED, OUTPUT);
pinMode(BUTTON0, INPUT);
pinMode(BUTTON1, INPUT);
pinMode(BUTTON2, INPUT);
strip.begin(); // this is declared in neopixel_mode
} // setup
void loop() {
// blink the builtin LED for diagnostics
now = millis();
if ((now - previous) > 1000) {
digitalWrite(BUILTIN_LED, !digitalRead(BUILTIN_LED));
previous = now;
}
// combine the three buttons in a single binary mode
uint8_t mode = (digitalRead(BUTTON0) << 0) | (digitalRead(BUTTON1) << 1) | (digitalRead(BUTTON2) << 2);
// update the Neopixel LED strip according to the current mode
switch (mode) {
case 0:
mode1(specification0);
break;
case 1:
mode4(specification1);
break;
case 2:
mode4(specification2);
break;
case 3:
mode4(specification3);
break;
case 4:
mode10(specification4);
break;
case 5:
mode10(specification5);
break;
case 6:
mode10(specification6);
break;
case 7:
mode12(specification7);
break;
}
} // loop
================================================
FILE: digispark_skateboard/neopixel_mode.cpp
================================================
/*
This is a subset of the color modes supported by my esp8266_artnet_neopixel sketch, see
https://github.com/robertoostenveld/arduino/tree/master/esp8266_artnet_neopixel
The functions have the same name and more or less similar functionality, but have been
simplified to save memory.
*/
#include "neopixel_mode.h"
#include "colorspace.h"
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB);
#define ENABLE_MODE1
#define ENABLE_MODE4
#define ENABLE_MODE10
#define ENABLE_MODE12
/************************************************************************************/
void mode1(uint8_t * data) {
#ifdef ENABLE_MODE1
int r, g, b;
r = data[0];
g = data[1];
b = data[2];
for (int pixel = 0; pixel < strip.numPixels(); pixel++)
strip.setPixelColor(pixel, r, g, b);
strip.show();
#endif
}
/************************************************************************************/
void mode4(uint8_t * data) {
#ifdef ENABLE_MODE4
int r, g, b;
// determine the current phase in the temporal cycle
float phase = CONFIG_SPEED * data[6] * millis() * 0.360;
phase = WRAP360(phase);
// pick between the two colors
if (phase < 180) {
r = data[0];
g = data[1];
b = data[2];
}
else {
r = data[3];
g = data[4];
b = data[5];
}
for (int pixel = 0; pixel < strip.numPixels(); pixel++)
strip.setPixelColor(pixel, r, g, b);
strip.show();
#endif
}
/************************************************************************************/
void mode10(uint8_t * data) {
#ifdef ENABLE_MODE10
int r, g, b;
// determine the current phase in the temporal cycle
float phase = CONFIG_SPEED * data[6] * millis() * 0.360;
for (int pixel = 0; pixel < strip.numPixels(); pixel++) {
float position = (360. * pixel / (strip.numPixels() - 1)) * CONFIG_SPLIT - phase;
position = WRAP360(position);
if (position < 180) {
r = data[0];
g = data[1];
b = data[2];
}
else {
r = data[3];
g = data[4];
b = data[5];
}
strip.setPixelColor(pixel, r, g, b);
}
strip.show();
#endif
};
/************************************************************************************/
void mode12(uint8_t * data) {
#ifdef ENABLE_MODE12
float saturation = 1. * data[0];
float value = 1. * data[1] ;
float speed = CONFIG_SPEED * data[2];
// determine the current phase in the temporal cycle
float phase = (speed * millis()) * 0.360;
for (int pixel = 0; pixel < strip.numPixels(); pixel++) {
float hue = (360. * pixel / strip.numPixels()) * CONFIG_SPLIT - phase;
hue = WRAP360(hue);
int r = hue; // hue, between 0-360
int g = saturation; // saturation, between 0-255
int b = value; // value, between 0-255
map_hsv_to_rgb(&r, &g, &b);
strip.setPixelColor(pixel, r, g, b);
}
strip.show();
#endif
};
/************************************************************************************/
void map_hsv_to_rgb(int *r, int *g, int *b) {
hsv in;
rgb out;
in.h = 360. * (*r) / 256.;
in.s = 1. * (*g) / 255.;
in.v = 1. * (*b) / 255.;
out = hsv2rgb(in);
(*r) = out.r * 255;
(*g) = out.g * 255;
(*b) = out.b * 255;
}
================================================
FILE: digispark_skateboard/neopixel_mode.h
================================================
#ifndef _NEOPIXEL_MODE_H_
#define _NEOPIXEL_MODE_H_
#include <Adafruit_NeoPixel.h>
#define PIN 0
#define NUMPIXELS 60
// in the original version these can be configured dynamically
#define CONFIG_SPEED 1.0
#define CONFIG_SPLIT 1
#define ROUND(x) (int(x + 0.5))
#define ABS(x) (x * (x < 0 ? -1 : 1))
#define MIN(x, y) (x < y ? x : y)
#define MAX(x, y) (x > y ? x : y)
#define WRAP360(x) (x > 0 ? (x - int((x)/360)*360) : (x - int((x)/360 - 1)*360)) // between 0 and 360
#define WRAP180(x) (WRAP360(x) < 180 ? WRAP360(x) : WRAP360(x) - 360) // between -180 and 180
#define BALANCE(l, x1, x2) ((x1) * (1. - l) + (x2) * l)
extern Adafruit_NeoPixel strip;
// the naming of these modes corresponds to my ESP8266 Artnet Neopixel module
void mode1(uint8_t *);
void mode4(uint8_t *);
void mode10(uint8_t *);
void mode12(uint8_t *);
void map_hsv_to_rgb(int *, int *, int *);
#endif
================================================
FILE: eegsynth_cvgate_mcp4725/README.md
================================================
# Arduino sketch for an MCP4725 digital-to-analog converter
This is a sketch to control Control Voltages (CV) and Gates for an analog synthesizer using an MCP4725 connected to an Arduino board.
See https://robertoostenveld.nl/usb-to-cvgate-converter-schematics-and-bill-of-materials/ for more details.
================================================
FILE: eegsynth_cvgate_mcp4725/eegsynth_cvgate_mcp4725.ino
================================================
/*
* EEGSynth Arduino based CV/Gate controller. This sketch allows
* one to use control voltages and gates to interface a computer
* through an Arduino with an analog synthesizer. The hardware
* comprises an Arduino Nano v3.0 with a MCP4725 12-bit DAC.
* Optionally it can be extended with a number of sample-and-hold
* ICs, a step-up voltage converter and some opamps.
*
* Some example sequences of characters are
* *c1v1024# control 1 voltage 5*1024/4095 = 1.25 V
* *g1v1# gate 1 value ON
*
* This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
* See http://creativecommons.org/licenses/by-sa/4.0/
*
* Copyright (C) 2015, Robert Oostenveld, http://www.eegsynth.org/
*/
#include <Wire.h>//Include the Wire library to talk I2C
#define voltage1pin 2 // the pin controlling the voltage output
#define voltage2pin 3 // the pin controlling the voltage output
#define voltage3pin 4 // the pin controlling the voltage output
#define voltage4pin 5 // the pin controlling the voltage output
#define gate1pin A0 // the pin controlling the digital gate
#define gate2pin A1 // the pin controlling the digital gate
#define gate3pin A2 // the pin controlling the digital gate
#define gate4pin A3 // the pin controlling the digital gate
#define MCP4726_CMD_WRITEDAC (0x40) // Writes data to the DAC
#define MCP4726_CMD_WRITEDACEEPROM (0x60) // Writes data to the DAC and the EEPROM (persisting the assigned value after reset)
#define NONE 0
#define VOLTAGE 1
#define GATE 2
#define OK 1
#define ERROR 2
//This is the I2C Address of the MCP4725, by default (A0 pulled to GND).
//Please note that this breakout is for the MCP4725A0.
//For devices with A0 pulled HIGH, use 0x61
#define MCP4725_ADDR 0x60
// these remember the state of all CV and gate outputs
int voltage1 = 0, voltage2 = 0, voltage3 = 0, voltage4 = 0;
int gate1 = 0, gate2 = 0, gate3 = 0, gate4 = 0;
int enable1 = 1, enable2 = 0, enable3 = 0, enable4 = 0;
void sampleAndHold(int pin, uint16_t value) {
uint8_t msb, lsb;
msb = (value / 16);
lsb = (value % 16) << 4;
Wire.beginTransmission(MCP4725_ADDR);
Wire.write(MCP4726_CMD_WRITEDAC); // cmd to update the DAC
Wire.write(msb); // the 8 most significant bits...
Wire.write(lsb); // the 4 least significant bits...
Wire.endTransmission();
digitalWrite(pin, HIGH);
delay(1); // give it some time to Sample and Hold
digitalWrite(pin, LOW);
return;
}
void setup() {
// initialize the serial communication:
Serial.begin(115200);
Serial.print("\n[eegsynth_cvgate / ");
Serial.print(__DATE__);
Serial.print(" / ");
Serial.print(__TIME__);
Serial.println("]");
Serial.setTimeout(1000);
Wire.begin();
// initialize the gate pins as output:
pinMode(gate1pin, OUTPUT);
pinMode(gate2pin, OUTPUT);
pinMode(gate3pin, OUTPUT);
pinMode(gate4pin, OUTPUT);
// initialize the control voltage pins as output:
pinMode(voltage1pin, OUTPUT);
pinMode(voltage2pin, OUTPUT);
pinMode(voltage3pin, OUTPUT);
pinMode(voltage4pin, OUTPUT);
}
void loop() {
byte b, channel = 0, command = NONE, status = OK;
int value = 0;
if (Serial.available()) {
// parse the input over the serial connection
b = Serial.read();
if (b == '*') {
Serial.readBytes(&b, 1);
if (b == 'c') {
command = VOLTAGE;
value = 0;
Serial.readBytes(&b, 1); channel = b - 48; // character '1' is ascii value 49
Serial.readBytes(&b, 1); // 'v'
Serial.readBytes(&b, 1); value = (b - 48) * 1000;
Serial.readBytes(&b, 1); value += (b - 48) * 100;
Serial.readBytes(&b, 1); value += (b - 48) * 10;
Serial.readBytes(&b, 1); value += (b - 48) * 1;
Serial.readBytes(&b, 1); command = (b == '#' ? command : NONE);
}
else if (b == 'g') {
command = GATE;
Serial.readBytes(&b, 1); channel = b - 48; // character '1' is ascii value 49
Serial.readBytes(&b, 1); // 'v'
Serial.readBytes(&b, 1); value = (b == '1');
Serial.readBytes(&b, 1); command = (b == '#' ? command : NONE);
}
else {
command = NONE;
}
}
else {
command = NONE;
}
// update the internal state of all output channels
if (command == VOLTAGE) {
switch (channel) {
case 1:
voltage1 = value;
status = (enable1 ? OK : ERROR);
break;
case 2:
voltage2 = value;
status = (enable2 ? OK : ERROR);
break;
case 3:
voltage3 = value;
status = (enable3 ? OK : ERROR);
break;
case 4:
voltage4 = value;
status = (enable4 ? OK : ERROR);
break;
default:
status = ERROR;
}
}
else if (command == GATE) {
switch (channel) {
case 1:
gate1 = (value != 0);
status = OK;
break;
case 2:
gate2 = (value != 0);
status = OK;
break;
case 3:
gate3 = (value != 0);
status = OK;
break;
case 4:
gate4 = (value != 0);
status = OK;
break;
default:
status = ERROR;
}
}
else {
status = ERROR;
}
if (status == OK)
Serial.println("ok");
else if (status == ERROR)
Serial.println("error");
}
else {
// refresh all enabled output channels
if (enable1) {
sampleAndHold(voltage1pin, voltage1);
digitalWrite(gate1pin, gate1);
}
if (enable2) {
sampleAndHold(voltage2pin, voltage2);
digitalWrite(gate2pin, gate2);
}
if (enable3) {
sampleAndHold(voltage3pin, voltage3);
digitalWrite(gate3pin, gate3);
}
if (enable4) {
sampleAndHold(voltage4pin, voltage4);
digitalWrite(gate4pin, gate4);
}
}
} //main
================================================
FILE: eegsynth_cvgate_mcp4822/eegsynth_cvgate_mcp4822.ino
================================================
/*
* EEGSynth Arduino based CV/Gate controller. This sketch allows
* one to use control voltages and gates to interface a computer
* through an Arduino with an analog synthesizer. The hardware
* comprises an Arduino Nano v3.0 with one or multiple MCP4822
* 12-bit DACs.
*
* Some example sequences of characters are
* *c1v1024# control 1 voltage 5*1024/4095 = 1.25 V
* *g1v1# gate 1 value ON
*
* This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
* See http://creativecommons.org/licenses/by-sa/4.0/
*
* Copyright (C) 2015, Robert Oostenveld, http://www.eegsynth.org/
*/
#include <SPI.h>
const int GAIN_1 = 0x1;
const int GAIN_2 = 0x0;
#define voltage12cs 2 // the slave-select pin for channel 1 and 2 on DAC #1
#define voltage34cs 3 // the slave-select pin for channel 3 and 4 on DAC #2
#define gate1pin A0 // the pin controlling the digital gate
#define gate2pin A1 // the pin controlling the digital gate
#define gate3pin A2 // the pin controlling the digital gate
#define gate4pin A3 // the pin controlling the digital gate
#define NONE 0
#define VOLTAGE 1
#define GATE 2
#define OK 1
#define ERROR 2
// these remember the state of all CV and gate outputs
int voltage1 = 0, voltage2 = 0, voltage3 = 0, voltage4 = 0;
int gate1 = 0, gate2 = 0, gate3 = 0, gate4 = 0;
const int enable1 = 1, enable2 = 1, enable3 = 1, enable4 = 1;
void setDacOutput(byte channel, byte gain, byte shutdown, unsigned int val)
{
byte lowByte = val & 0xff;
byte highByte = ((val >> 8) & 0xff) | channel << 7 | gain << 5 | shutdown << 4;
// note that we are not using the default pin 10 for slave select, see further down
// PORTB &= 0xfb; // toggle pin 10 as Slave Select low
SPI.transfer(highByte);
SPI.transfer(lowByte);
// PORTB |= 0x4; // toggle pin 10 as Slave Select high
}
void setup() {
// initialize the serial communication:
Serial.begin(115200);
Serial.print("\n[eegsynth_cvgate_mcp4822 / ");
Serial.print(__DATE__);
Serial.print(" / ");
Serial.print(__TIME__);
Serial.println("]");
Serial.setTimeout(1000);
SPI.begin();
// initialize the gate pins as output:
pinMode(gate1pin, OUTPUT);
pinMode(gate2pin, OUTPUT);
pinMode(gate3pin, OUTPUT);
pinMode(gate4pin, OUTPUT);
// initialize the control voltage pins as output:
pinMode(voltage12cs, OUTPUT);
pinMode(voltage34cs, OUTPUT);
// default is to disable the communication with all SPI devices
digitalWrite(voltage12cs, HIGH);
digitalWrite(voltage34cs, HIGH);
}
void loop() {
byte b, channel = 0, command = NONE, status = OK;
int value = 0;
if (Serial.available()) {
// parse the input over the serial connection
b = Serial.read();
if (b == '*') {
Serial.readBytes(&b, 1);
if (b == 'c') {
command = VOLTAGE;
value = 0;
Serial.readBytes(&b, 1); channel = b - 48; // character '1' is ascii value 49
Serial.readBytes(&b, 1); // 'v'
Serial.readBytes(&b, 1); value = (b - 48) * 1000;
Serial.readBytes(&b, 1); value += (b - 48) * 100;
Serial.readBytes(&b, 1); value += (b - 48) * 10;
Serial.readBytes(&b, 1); value += (b - 48) * 1;
Serial.readBytes(&b, 1); command = (b == '#' ? command : NONE);
}
else if (b == 'g') {
command = GATE;
Serial.readBytes(&b, 1); channel = b - 48; // character '1' is ascii value 49
Serial.readBytes(&b, 1); // 'v'
Serial.readBytes(&b, 1); value = (b == '1');
Serial.readBytes(&b, 1); command = (b == '#' ? command : NONE);
}
else {
command = NONE;
}
}
else {
command = NONE;
}
// update the internal state of all output channels
if (command == VOLTAGE) {
switch (channel) {
case 1:
voltage1 = value;
status = (enable1 ? OK : ERROR);
break;
case 2:
voltage2 = value;
status = (enable2 ? OK : ERROR);
break;
case 3:
voltage3 = value;
status = (enable3 ? OK : ERROR);
break;
case 4:
voltage4 = value;
status = (enable4 ? OK : ERROR);
break;
default:
status = ERROR;
}
}
else if (command == GATE) {
switch (channel) {
case 1:
gate1 = (value != 0);
status = OK;
break;
case 2:
gate2 = (value != 0);
status = OK;
break;
case 3:
gate3 = (value != 0);
status = OK;
break;
case 4:
gate4 = (value != 0);
status = OK;
break;
default:
status = ERROR;
}
}
else {
status = ERROR;
}
if (status == OK)
Serial.println("ok");
else if (status == ERROR)
Serial.println("error");
}
else {
// refresh all enabled output channels
if (enable1) {
digitalWrite(voltage12cs, LOW); // enable slave select
setDacOutput(0, GAIN_2, 1, voltage1);
digitalWrite(voltage12cs, HIGH); // disable slave select
digitalWrite(gate1pin, gate1);
}
if (enable2) {
digitalWrite(voltage12cs, LOW); // enable slave select
setDacOutput(1, GAIN_2, 1, voltage2);
digitalWrite(voltage12cs, HIGH); // disable slave select
digitalWrite(gate2pin, gate2);
}
if (enable3) {
digitalWrite(voltage34cs, LOW); // enable slave select
setDacOutput(0, GAIN_2, 1, voltage3);
digitalWrite(voltage34cs, HIGH); // disable slave select
digitalWrite(gate3pin, gate3);
}
if (enable4) {
digitalWrite(voltage34cs, LOW); // enable slave select
setDacOutput(1, GAIN_2, 1, voltage4);
digitalWrite(voltage34cs, HIGH); // disable slave select
digitalWrite(gate4pin, gate4);
}
}
} //main
================================================
FILE: eegsynth_devirtualizer/eegsynth_devirtualizer.ino
================================================
/*
* EEGSynth Arduino based CV/Gate controller. This sketch allows
* one to use control voltages and gates to interface a computer
* through an Arduino with an analog synthesizer. The hardware
* comprises an Arduino Nano v3.0 with one or multiple MCP4822
* 12-bit DACs.
*
* Some example sequences of characters are
* *c1v1024# control 1 voltage 5*1024/4095 = 1.25 V
* *g1v1# gate 1 value ON
*
* This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
* See http://creativecommons.org/licenses/by-sa/4.0/
*
* Copyright (C) 2015, Robert Oostenveld, http://www.eegsynth.org/
*/
#include <SPI.h>
const int GAIN_1 = 0x1;
const int GAIN_2 = 0x0;
#define voltage12cs A0 // the slave-select pin for channel 1 and 2 on DAC #1
#define voltage34cs A1 // the slave-select pin for channel 3 and 4 on DAC #2
#define voltage56cs A2 // the slave-select pin for channel 1 and 2 on DAC #1
#define voltage78cs A3 // the slave-select pin for channel 3 and 4 on DAC #2
#define gate1pin 2 // the pin controlling the digital gate
#define gate2pin 3 // the pin controlling the digital gate
#define gate3pin 4 // the pin controlling the digital gate
#define gate4pin 5 // the pin controlling the digital gate
#define gate5pin 6 // the pin controlling the digital gate
#define gate6pin 7 // the pin controlling the digital gate
#define gate7pin 8 // the pin controlling the digital gate
#define gate8pin 9 // the pin controlling the digital gate
#define NONE 0
#define VOLTAGE 1
#define GATE 2
#define OK 1
#define ERROR 2
// these remember the state of all CV and gate outputs
int voltage1 = 0, voltage2 = 0, voltage3 = 0, voltage4 = 0, voltage5, voltage6, voltage7, voltage8;
int gate1 = 0, gate2 = 0, gate3 = 0, gate4 = 0, gate5, gate6, gate7, gate8;
const int enable1 = 1, enable2 = 1, enable3 = 1, enable4 = 1, enable5 = 1, enable6 = 1, enable7 = 1, enable8 = 1;
void setDacOutput(byte channel, byte gain, byte shutdown, unsigned int val)
{
byte lowByte = val & 0xff;
byte highByte = ((val >> 8) & 0xff) | channel << 7 | gain << 5 | shutdown << 4;
// note that we are not using the default pin 10 for slave select, see further down
// PORTB &= 0xfb; // toggle pin 10 as Slave Select low
SPI.transfer(highByte);
SPI.transfer(lowByte);
// PORTB |= 0x4; // toggle pin 10 as Slave Select high
}
void setup() {
// initialize the serial communication:
Serial.begin(115200);
Serial.print("\n[eegsynth_devirtualizer / ");
Serial.print(__DATE__);
Serial.print(" / ");
Serial.print(__TIME__);
Serial.println("]");
Serial.setTimeout(1000);
SPI.begin();
// initialize the gate pins as output:
pinMode(gate1pin, OUTPUT);
pinMode(gate2pin, OUTPUT);
pinMode(gate3pin, OUTPUT);
pinMode(gate4pin, OUTPUT);
pinMode(gate5pin, OUTPUT);
pinMode(gate6pin, OUTPUT);
pinMode(gate7pin, OUTPUT);
pinMode(gate8pin, OUTPUT);
// initialize the control voltage pins as output:
pinMode(voltage12cs, OUTPUT);
pinMode(voltage34cs, OUTPUT);
pinMode(voltage56cs, OUTPUT);
pinMode(voltage78cs, OUTPUT);
// default is to disable the communication with all SPI devices
digitalWrite(voltage12cs, HIGH);
digitalWrite(voltage34cs, HIGH);
digitalWrite(voltage56cs, HIGH);
digitalWrite(voltage78cs, HIGH);
}
void loop() {
byte b, channel = 0, command = NONE, status = OK;
int value = 0;
if (Serial.available()) {
// parse the input over the serial connection
b = Serial.read();
if (b == '*') {
Serial.readBytes(&b, 1);
if (b == 'c') {
command = VOLTAGE;
value = 0;
Serial.readBytes(&b, 1); channel = b - 48; // character '1' is ascii value 49
Serial.readBytes(&b, 1); // 'v'
Serial.readBytes(&b, 1); value = (b - 48) * 1000;
Serial.readBytes(&b, 1); value += (b - 48) * 100;
Serial.readBytes(&b, 1); value += (b - 48) * 10;
Serial.readBytes(&b, 1); value += (b - 48) * 1;
Serial.readBytes(&b, 1); command = (b == '#' ? command : NONE);
}
else if (b == 'g') {
command = GATE;
Serial.readBytes(&b, 1); channel = b - 48; // character '1' is ascii value 49
Serial.readBytes(&b, 1); // 'v'
Serial.readBytes(&b, 1); value = (b == '1');
Serial.readBytes(&b, 1); command = (b == '#' ? command : NONE);
}
else {
command = NONE;
}
}
else {
command = NONE;
}
// update the internal state of all output channels
if (command == VOLTAGE) {
switch (channel) {
case 1:
voltage1 = value;
status = (enable1 ? OK : ERROR);
break;
case 2:
voltage2 = value;
status = (enable2 ? OK : ERROR);
break;
case 3:
voltage3 = value;
status = (enable3 ? OK : ERROR);
break;
case 4:
voltage4 = value;
status = (enable4 ? OK : ERROR);
break;
case 5:
voltage5 = value;
status = (enable5 ? OK : ERROR);
break;
case 6:
voltage6 = value;
status = (enable6 ? OK : ERROR);
break;
case 7:
voltage7 = value;
status = (enable7 ? OK : ERROR);
break;
case 8:
voltage8 = value;
status = (enable8 ? OK : ERROR);
break;
default:
status = ERROR;
}
}
else if (command == GATE) {
switch (channel) {
case 1:
gate1 = (value != 0);
status = OK;
break;
case 2:
gate2 = (value != 0);
status = OK;
break;
case 3:
gate3 = (value != 0);
status = OK;
break;
case 4:
gate4 = (value != 0);
status = OK;
break;
case 5:
gate5 = (value != 0);
status = OK;
break;
case 6:
gate6 = (value != 0);
status = OK;
break;
case 7:
gate7 = (value != 0);
status = OK;
break;
case 8:
gate8 = (value != 0);
status = OK;
break;
default:
status = ERROR;
}
}
else {
status = ERROR;
}
if (status == OK)
Serial.println("ok");
else if (status == ERROR)
Serial.println("error");
}
else {
// refresh all enabled output channels
if (enable1) {
digitalWrite(voltage12cs, LOW); // enable slave select
setDacOutput(0, GAIN_2, 1, voltage1);
digitalWrite(voltage12cs, HIGH); // disable slave select
digitalWrite(gate1pin, gate1);
}
if (enable2) {
digitalWrite(voltage12cs, LOW); // enable slave select
setDacOutput(1, GAIN_2, 1, voltage2);
digitalWrite(voltage12cs, HIGH); // disable slave select
digitalWrite(gate2pin, gate2);
}
if (enable3) {
digitalWrite(voltage34cs, LOW); // enable slave select
setDacOutput(0, GAIN_2, 1, voltage3);
digitalWrite(voltage34cs, HIGH); // disable slave select
digitalWrite(gate3pin, gate3);
}
if (enable4) {
digitalWrite(voltage34cs, LOW); // enable slave select
setDacOutput(1, GAIN_2, 1, voltage4);
digitalWrite(voltage34cs, HIGH); // disable slave select
digitalWrite(gate4pin, gate4);
}
if (enable5) {
digitalWrite(voltage56cs, LOW); // enable slave select
setDacOutput(1, GAIN_2, 1, voltage5);
digitalWrite(voltage56cs, HIGH); // disable slave select
digitalWrite(gate5pin, gate5);
}
if (enable6) {
digitalWrite(voltage56cs, LOW); // enable slave select
setDacOutput(1, GAIN_2, 1, voltage6);
digitalWrite(voltage56cs, HIGH); // disable slave select
digitalWrite(gate6pin, gate6);
}
if (enable7) {
digitalWrite(voltage78cs, LOW); // enable slave select
setDacOutput(1, GAIN_2, 1, voltage7);
digitalWrite(voltage78cs, HIGH); // disable slave select
digitalWrite(gate7pin, gate7);
}
if (enable8) {
digitalWrite(voltage78cs, LOW); // enable slave select
setDacOutput(1, GAIN_2, 1, voltage8);
digitalWrite(voltage78cs, HIGH); // disable slave select
digitalWrite(gate8pin, gate8);
}
}
} //main
================================================
FILE: eegsynth_usbdmxpro/COPYING.txt
================================================
/*==============================================================================
Copyright (c) 2013 Soixante circuits
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
==============================================================================*/
================================================
FILE: eegsynth_usbdmxpro/eegsynth_usbdmxpro.ino
================================================
/*
The purpose of this sketch is to implement a module that converts from USB to DMX512.
This allows to use computer software to control stage lighting lamps
This sketch is partially compatible with the Enttec DMX USB Pro module, but the startup
sequence of this Arduino sketch is different and other/commercial software is likely not
to recognize the module as DMX USB Pro .
Components
- Arduino Nano or compatible 5V board, e.g. http://ebay.to/2iAeUON
- MAX485 module, e.g. http://ebay.to/2iuKQlr
- 3 or 5 pin female XLR connector
Wiring scheme
- connect 3.3V and GND from the Arduino to Vcc and GND of the MAX485 module
- connect pin DE (data enable) and RE (receive enable) of the MAX485 module to 3.3V
- connect pin D2 of the Arduino to the DI (data in) pin of the MAX485 module
- connect pin A to XLR 3
- connect pin B to XLR 2
- connect GND to XLR 1
*/
#include <DmxSimple.h>
#define DI_PIN 2 // data out of the Arduino, data in to the MAX485
// these are defined in https://www.enttec.com/docs/dmx_usb_pro_api_spec.pdf
#define DMX_PRO_START_MSG 126 // 0x7E
#define DMX_PRO_END_MSG 231 // 0xE7
// the packets can have the following labels, i.e. content
#define DMX_PRO_REPROGRAM_FIRMWARE 1
#define DMX_PRO_FLASH_PAGE 2
#define DMX_PRO_GET_WIDGET_PARAM 3
#define DMX_PRO_SET_WIDGET_PARAM 4
#define DMX_PRO_RECEIVED_DMX 5
#define DMX_PRO_SEND_DMX 6
#define DMX_PRO_SEND_RDM 7
#define DMX_PRO_RECEIVE_DMX_ON_CHANGE 8
#define DMX_PRO_RECEIVED_DMX_CHANGE 9
#define DMX_PRO_GET_SERIAL_NUMBER 10
#define DMX_PRO_SENT_DRM_DISCOVERY 11
// each DMX data packet starts with this code
#define DMX_PRO_START_CODE 0
// these are some local states
#define DMX_PRO_SEND_DMX_LSB 240
#define DMX_PRO_SEND_DMX_MSB 241
#define DMX_PRO_SEND_DMX_DATA 242
unsigned char state = DMX_PRO_END_MSG;
unsigned int dataSize;
unsigned int channel;
void setup() {
DmxSimple.usePin(DI_PIN);
Serial.begin(57600);
while (!Serial);
}
void loop() {
while (!Serial.available())
yield();
unsigned char c = Serial.read();
if (c == DMX_PRO_START_MSG && state != DMX_PRO_END_MSG) {
state = c; // this should not happen, it means that part of the previous packet was lost
}
if (c == DMX_PRO_START_MSG && state == DMX_PRO_END_MSG) {
state = c;
}
else if (c == DMX_PRO_SEND_DMX && state == DMX_PRO_START_MSG) {
state = c;
}
else if (state == DMX_PRO_SEND_DMX) {
dataSize = c & 0xff;
state = DMX_PRO_SEND_DMX_LSB;
}
else if (state == DMX_PRO_SEND_DMX_LSB) {
dataSize += (c << 8) & 0xff00;
state = DMX_PRO_SEND_DMX_MSB;
}
else if (state == DMX_PRO_SEND_DMX_MSB && c == DMX_PRO_START_CODE) {
state = DMX_PRO_SEND_DMX_DATA;
channel = 1;
}
else if (state == DMX_PRO_SEND_DMX_DATA && channel < dataSize) {
DmxSimple.write(channel, c);
channel++;
}
else if (state == DMX_PRO_SEND_DMX_DATA && channel == dataSize && c == DMX_PRO_END_MSG) {
state = c;
}
}
================================================
FILE: esp32_3wd_servo/README.md
================================================
# Three-wheel-drive omni-wheel robot platform
This is an Arduino sketch for an ESP32 (LOLIN32 Lite) controlled
three-wheel-drive omni-wheel robot based on servo motors, similar
to the one documented [here][1] and at various other places online.
The motors are continuous rotation TS90D servo motors, i.e., these
rotate at a given speed, not towards a given location like normal
servo motors.
The control of the robot is implemented using Open Sound Control
(OSC), using the [TouchOSC][5] app on my iPhone. Actually, the
firmware version in this sketch should not (yet) be considered a
robot since there is no autonomous movement, but more like a
radio-controlled car.
The reason for me **not continuing** the development of this robot
platform is that the servo motors are too noisy for the intended
purpose, and that they stall when running them at a too low speed.
I switched to using 28BYJ-48 stepper motors; the sketch for that
can be found elsewhere in this repository.
[1]: https://github.com/manav20/3-wheel-omni
[2]: https://en.wikipedia.org/wiki/Omni_wheel
[3]: https://www.piscinarobots.nl/robots-y-kits/38mm%20(1.5%20inch)%20double%20plastic%20omni%20wheel%20(compatible%20met%20servo%20motor%20)%20-%2014184
[4]: https://nl.aliexpress.com/item/32478938051.html
[5]: https://hexler.net
================================================
FILE: esp32_3wd_servo/esp32_3wd_servo.ino
================================================
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
#include <WiFiUdp.h>
#include <OSCMessage.h> // https://github.com/CNMAT/OSC
#include <ESP32Servo.h>
#include <math.h>
WiFiUDP Udp;
OSCErrorCode error;
Servo servo1, servo2, servo3;
const char *host = "3WD-SERVO";
const char *version = __DATE__ " / " __TIME__;
const unsigned int inPort = 8000; // local OSC port
const unsigned int outPort = 9000; // remote OSC port (not used here)
// GPIO connections to the servo control
const int servo1_pin = 25;
const int servo2_pin = 26;
const int servo3_pin = 27;
// published values for the TS90D servo, neutral is 1500 uS
const int minUs = 500;
const int maxUs = 2500;
const float pr = 0.06; // platform radius, distance from the platform center to each of the wheels, in meter
float vx = 0, vy = 0, vt = 0; // speed in meter per second, and in radians per second
float x = 0, y = 0, theta = 0; // absolute position (in meter) and rotation (in radians)
float r1 = 0, r2 = 0, r3 = 0; // speed of the stepper motors, in steps per second
// don't use the built-in version of map(), as that is only for long int values
// see https://docs.arduino.cc/language-reference/en/functions/math/map/
#define map(value, fromLow, fromHigh, toLow, toHigh) ((toHigh - toLow) * (value - fromLow) / (fromHigh - fromLow) + toLow)
void printCallback(OSCMessage &msg) {
Serial.print(msg.getAddress());
Serial.print(" : ");
for (int i = 0; i < msg.size(); i++) {
if (msg.isInt(i)) {
Serial.print(msg.getInt(i));
} else if (msg.isFloat(i)) {
Serial.print(msg.getFloat(i));
} else if (msg.isDouble(i)) {
Serial.print(msg.getDouble(i));
} else if (msg.isBoolean(i)) {
Serial.print(msg.getBoolean(i));
} else if (msg.isString(i)) {
char buffer[256];
msg.getString(i, buffer);
Serial.print(buffer);
} else {
Serial.print("?");
}
if (i < (msg.size() - 1)) {
Serial.print(", "); // there are more to come
}
}
Serial.println();
}
void accxyzCallback(OSCMessage &msg) {
vx = msg.getFloat(0);
vy = msg.getFloat(1);
vt = 0;
updateSpeed();
}
void updateSpeed() {
// convert the speed in world-coordinates into rotation speed of the motors and into servo controls
// see https://github.com/manav20/3-wheel-omni
// pr is the platform radius, i.e. the distance of the center of the platform to each wheel
// vx, vy, and vt is the speed in world-coordinates
// r1, r2, and r3 is the rotation speed of the three motors
// theta is the heading of the robot (FIXME, needs to be integrated over time)
r1 = -sin(theta) * vx + cos(theta) * vy + pr * vt;
r2 = -sin(PI / 3 - theta) * vx - cos(PI / 3 - theta) * vy + pr * vt;
r3 = sin(PI / 3 + theta) * vx - cos(PI / 3 + theta) * vy + pr * vt;
// convert the rotation speed of the motors into servo control values between 0 and 180, where 90 is neutral
r1 = map(r1, -1, 1, 0, 180);
r2 = map(r2, -1, 1, 0, 180);
r3 = map(r3, -1, 1, 0, 180);
servo1.write(r1);
servo2.write(r2);
servo3.write(r3);
Serial.print(r1);
Serial.print(", ");
Serial.print(r2);
Serial.print(", ");
Serial.print(r3);
Serial.println();
}
void wheel1Callback(OSCMessage &msg) {
r1 = msg.getFloat(0);
servo1.write(r1);
}
void wheel2Callback(OSCMessage &msg) {
r2 = msg.getFloat(0);
servo2.write(r2);
}
void wheel3Callback(OSCMessage &msg) {
r3 = msg.getFloat(0);
servo3.write(r3);
}
void setup() {
Serial.begin(115200);
Serial.print("[ ");
Serial.print(host);
Serial.print(" / ");
Serial.print(version);
Serial.println(" ]");
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
WiFi.hostname(host);
WiFi.begin();
WiFiManager wifiManager;
wifiManager.setAPStaticIPConfig(IPAddress(192, 168, 1, 1), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));
wifiManager.autoConnect(host);
Serial.println("connected");
// Used for OSC
Udp.begin(inPort);
servo1.attach(servo1_pin, minUs, maxUs);
servo2.attach(servo2_pin, minUs, maxUs);
servo3.attach(servo3_pin, minUs, maxUs);
}
void loop() {
OSCMessage msg;
int size = Udp.parsePacket();
if (size > 0) {
while (size--) {
msg.fill(Udp.read());
}
if (!msg.hasError()) {
msg.dispatch("/accxyz", accxyzCallback);
msg.dispatch("/wheel1", wheel1Callback);
msg.dispatch("/wheel2", wheel2Callback);
msg.dispatch("/wheel3", wheel3Callback);
msg.dispatch("/*/*/*", printCallback) || msg.dispatch("/*/*", printCallback) || msg.dispatch("/*", printCallback);
} else {
error = msg.getError();
Serial.print("error: ");
Serial.println(error);
}
}
}
================================================
FILE: esp32_3wd_stepper/README.md
================================================
# Three-wheel-drive omni-wheel robot platform
This is an Arduino sketch for an ESP32 (LOLIN32 Lite) controlled three-wheel-drive omni-wheel robot based on 28BYJ-48 stepper motors, similar to the one documented [here][1] and various other places online.
The robot can rotate in-place and move in any direction using [omni wheels][2]. I ordered 38 mm diameter wheels from [Piscinarobots][3], but they are also available on [AliExpress][4].
To position the motors and wheels at 120 degree angles, I designed and 3D-printed a circular base. I also designed and 3D-printed the first set of omni-wheels following the dimensions of the commercial 38 mm wheels, but found that those were too rough and not running sideways smoothly enough. Hence I switched to the commercially fabricated wheels.
The control of the robot is implemented using Open Sound Control (OSC), using the [TouchOSC][5] app on my iPhone. The robot can move by itself along a prespecified list of waypoints. The waypoints are uploaded as tabular CSV file, which also contains a column with the time at which each waypoint is to be reached.
## Coordinate system
This sketch aligns with the convention used by [WPILib][6] and uses an NWU axes convention (North-West-Up as external reference in the world frame.) In the NWU axes convention, the positive X axis points ahead, the positive Y axis points left, and the positive Z axis points up referenced from the floor. When viewed with each positive axis pointing toward you, counter-clockwise (CCW) is a positive value and clockwise (CW) is a negative value.
Considering the 3-wheel-drive robot platform, wheel 1 is placed at the front (the "nose") and wheel 2 and 3 at the left and right back. The x-axis is pointing fron the center to wheel 1, and the y-axix pointing to the left. The angle theta is positive when (seen from the top) the robot turns to the left (CCW) and negative to the right.
## Settings
In the settings menu you can change the following parameters.
### Repeat
This can be 0 (false) or 1 (true) and specifies whether a route should be repeated when the end of the route is reached.
### Absolute
This can be 0 (false) for relative coordinates) or 1 (true) for absolute coordinates.
When configured as relative, you specify the waypoints and movements _relative_ to the robot's position at the moment each route starts. The starting position (and angle) is set to zero and every new route is executed without taking any previously executed routes into consideration.
When configured as absolute, you specify all waypoints relative to the robot's initial position and orientation in the room. WHen starting a new route, the latest known position (and orientation) is taken as the starting point.
### Warp
This is a floating point number by which the time and distances are scaled. By default the warp is 1.00.
If you specify the warp as 2.00, all positions along the route will be multiplied by a factor of 2x and the time between waypoints will be also be multiplied by a factor of 2x. Consequently, the speed for each segment will remain the same, and the overall route will be 2x larger and will take 2x longer.
If you specify the warp as 0.5, the overall route will be 2x shorter and faster
### Serial
This can be 0 (false) or 1 (true) and specifies how much information will be printed on the serial console when the robot is connected to a computer. This is only for debugging.
## Links
[1]: https://github.com/manav20/3-wheel-omni
[2]: https://en.wikipedia.org/wiki/Omni_wheel
[3]: https://www.piscinarobots.nl/robots-y-kits/38mm%20(1.5%20inch)%20double%20plastic%20omni%20wheel%20(compatible%20met%20servo%20motor%20)%20-%2014184
[4]: https://nl.aliexpress.com/item/32478938051.html
[5]: https://hexler.net
[6]: https://docs.wpilib.org/en/stable/docs/software/basic-programming/coordinate-system.html
================================================
FILE: esp32_3wd_stepper/blink_led.cpp
================================================
#include "blink_led.h"
Ticker blinker;
enum {
LED_ON,
LED_OFF,
LED_SLOW,
LED_MEDIUM,
LED_FAST,
} ledState;
void changeState() {
digitalWrite(LED, !(digitalRead(LED))); // Invert the current state of the LED
}
void ledInit() {
pinMode(LED, OUTPUT);
}
void ledOn() {
if (ledState != LED_ON) {
ledState = LED_ON;
blinker.detach();
digitalWrite(LED, LOW);
}
}
void ledOff() {
if (ledState != LED_OFF) {
ledState = LED_OFF;
blinker.detach();
digitalWrite(LED, HIGH);
}
}
void ledSlow() {
if (ledState != LED_SLOW) {
ledState = LED_SLOW;
blinker.detach();
blinker.attach_ms(1000, changeState);
}
}
void ledMedium() {
if (ledState != LED_MEDIUM) {
ledState = LED_MEDIUM;
blinker.detach();
blinker.attach_ms(250, changeState);
}
}
void ledFast() {
if (ledState != LED_FAST) {
ledState = LED_FAST;
blinker.detach();
blinker.attach_ms(100, changeState);
}
}
================================================
FILE: esp32_3wd_stepper/blink_led.h
================================================
#ifndef _BLINK_LED_H_
#define _BLINK_LED_H_
#include <Arduino.h>
#include <Ticker.h>
#define LED 22 // GPIO22 is connected to the builtin LED on the Lolin32 Lite
void ledInit(void);
void ledOn(void);
void ledOff(void);
void ledSlow(void);
void ledMedium(void);
void ledFast(void);
#endif
================================================
FILE: esp32_3wd_stepper/data/config.json
================================================
{
"repeat":0,
"absolute":0,
"warp": 1,
"debug":0
}
================================================
FILE: esp32_3wd_stepper/data/hello.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Hello</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<p>Hello tortoise!</p>
<img src="tortoise.jpg">
</body>
</html>
================================================
FILE: esp32_3wd_stepper/data/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Index</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<a href="/hello.html">Hello</a></br>
<a href="/monitor.html">Monitor</a></br>
<a href="/settings.html">Change settings</a></br>
<a href="/waypoints.html">Edit waypoints</a></br>
<a href="/reconnect">Reconnect WiFi</a></br>
<a href="/defaults">Default settings</a></br>
<a href="/restart">Restart hardware</a></br>
</body>
</html>
================================================
FILE: esp32_3wd_stepper/data/monitor.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Monitor</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>Monitor</h1>
Uptime:
<div id="uptime" name"uptime">?</div>
Firmware version:
<div id="version" name="version">?</div>
MAC address:
<div id="macaddress" name"macaddress">?</div>
<script>
async function updateContent() {
const response = await fetch("json");
const data = await response.json();
console.log(data);
document.getElementById("uptime").innerHTML = data["uptime"];
document.getElementById("version").innerHTML = data["version"];
document.getElementById("macaddress").innerHTML = data["macaddress"];
}
updateContent();
</script>
</body>
</html>
================================================
FILE: esp32_3wd_stepper/data/reload_failure.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Failure</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<p>Failure</p>
<script type="text/javascript">
setTimeout(function(){location="/index.html"},1000);
</script>
</body>
</html>
================================================
FILE: esp32_3wd_stepper/data/reload_success.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Success</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<p>Success</p>
<script type="text/javascript">
setTimeout(function(){location="/index.html"},1000);
</script>
</body>
</html>
================================================
FILE: esp32_3wd_stepper/data/settings.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Settings</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>Settings</h1>
<form id="settings-form" method="post" action="json">
<div class="field">
<label for="repeat">repeat:</label><br>
<input type="text" id="repeat" name="repeat" value="?" required>
</div>
<div class="field">
<label for="absolute">absolute:</label><br>
<input type="text" id="absolute" name="absolute" value="?" required>
</div>
<div class="field">
<label for="warp">warp:</label><br>
<input type="text" id="warp" name="warp" value="?" required>
</div>
<div class="field">
<label for="debug">debug:</label><br>
<input type="text" id="debug" name="debug" value="?" required>
</div>
<div class="field">
<button type="submit">Save</button>
</div>
</form>
<script>
async function updateContent() {
const response = await fetch("json");
const data = await response.json();
console.log(data);
document.getElementById("repeat").value = data["repeat"];
document.getElementById("absolute").value = data["absolute"];
document.getElementById("warp").value = data["warp"];
document.getElementById("debug").value = data["debug"];
}
updateContent();
</script>
</body>
</html>
================================================
FILE: esp32_3wd_stepper/data/style.css
================================================
.c{
text-align: center;
}
div,input{
padding:5px;font-size:1em;
}
input{
width:95%;
}
body{
text-align: center;font-family:verdana;
}
button{
border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;
}
.q{
float: right;width: 64px;text-align: right;
}
.l{
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAALVBMVEX///8EBwfBwsLw8PAzNjaCg4NTVVUjJiZDRUUUFxdiZGSho6OSk5Pg4eFydHTCjaf3AAAAZElEQVQ4je2NSw7AIAhEBamKn97/uMXEGBvozkWb9C2Zx4xzWykBhFAeYp9gkLyZE0zIMno9n4g19hmdY39scwqVkOXaxph0ZCXQcqxSpgQpONa59wkRDOL93eAXvimwlbPbwwVAegLS1HGfZAAAAABJRU5ErkJggg==") no-repeat left center;background-size: 1em;
}
================================================
FILE: esp32_3wd_stepper/data/waypoints.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Edit waypoints</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>Edit waypoints</h1>
<form id="waypoints-form" method="post" action="json">
<div class="field">
<label for="waypoints1">waypoints route 1:</label><br>
<textarea type="text" rows="10" cols="80" id="waypoints1" name="waypoints1" required>?</textarea>
</div>
<div class="field">
<label for="waypoints2">waypoints route 2:</label><br>
<textarea type="text" rows="10" cols="80" id="waypoints2" name="waypoints2" required>?</textarea>
</div>
<div class="field">
<label for="waypoints3">waypoints route 3:</label><br>
<textarea type="text" rows="10" cols="80" id="waypoints3" name="waypoints3" required>?</textarea>
</div>
<div class="field">
<label for="waypoints4">waypoints route 4:</label><br>
<textarea type="text" rows="10" cols="80" id="waypoints4" name="waypoints4" required>?</textarea>
</div>
<div class="field">
<label for="waypoints5">waypoints route 5:</label><br>
<textarea type="text" rows="10" cols="80" id="waypoints5" name="waypoints5" required>?</textarea>
</div>
<div class="field">
<label for="waypoints6">waypoints route 6:</label><br>
<textarea type="text" rows="10" cols="80" id="waypoints6" name="waypoints6" required>?</textarea>
</div>
<div class="field">
<label for="waypoints7">waypoints route 7:</label><br>
<textarea type="text" rows="10" cols="80" id="waypoints7" name="waypoints7" required>?</textarea>
</div>
<div class="field">
<label for="waypoints8">waypoints route 8:</label><br>
<textarea type="text" rows="10" cols="80" id="waypoints8" name="waypoints8" required>?</textarea>
</div>
<div class="field">
<button type="submit">Save</button>
</div>
</form>
<script>
async function updateContent() {
const response = await fetch("json");
const data = await response.json();
console.log(data);
document.getElementById("waypoints1").value = data["waypoints1"];
document.getElementById("waypoints2").value = data["waypoints2"];
document.getElementById("waypoints3").value = data["waypoints3"];
document.getElementById("waypoints4").value = data["waypoints4"];
document.getElementById("waypoints5").value = data["waypoints5"];
document.getElementById("waypoints6").value = data["waypoints6"];
document.getElementById("waypoints7").value = data["waypoints7"];
document.getElementById("waypoints8").value = data["waypoints8"];
}
updateContent();
</script>
</body>
</html>
================================================
FILE: esp32_3wd_stepper/data/waypoints1.csv
================================================
0,0,0,0
================================================
FILE: esp32_3wd_stepper/data/waypoints2.csv
================================================
0,0,0,0
================================================
FILE: esp32_3wd_stepper/data/waypoints3.csv
================================================
0,0,0,0
================================================
FILE: esp32_3wd_stepper/data/waypoints4.csv
================================================
0,0,0,0
================================================
FILE: esp32_3wd_stepper/data/waypoints5.csv
================================================
0,0,0,0
================================================
FILE: esp32_3wd_stepper/data/waypoints6.csv
================================================
0,0,0,0
================================================
FILE: esp32_3wd_stepper/data/waypoints7.csv
================================================
0,0,0,0
================================================
FILE: esp32_3wd_stepper/data/waypoints8.csv
================================================
0,0,0,0
================================================
FILE: esp32_3wd_stepper/esp32_3wd_stepper.ino
================================================
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
#include <WiFiUdp.h>
#include <ESPmDNS.h>
#include <math.h>
#include "webinterface.h"
#include "waypoints.h"
#include "parseosc.h"
#include "stepper.h"
#include "blink_led.h"
#include "util.h"
const char *host = "3WD-STEPPER";
const char *version = __DATE__ " / " __TIME__;
WebServer server(80);
WiFiUDP Udp;
Stepper wheel1;
Stepper wheel2;
Stepper wheel3;
const unsigned int inPort = 8000; // local OSC port for receiving commands
const float pd = 0.084252; // platform diameter, in meter
const float wd = 0.038; // wheel diameter, in meter
const float pc = pd * M_PI; // platform circumference, in meter
const float wc = wd * M_PI; // wheel circumference, in meter
const float totalsteps = 2048; // steps per revolution, see http://www.mjblythe.com/hacks/2016/09/28byj-48-stepper-motor/
const float maxsteps = 512; // maximum steps per seconds, determined experimentally
unsigned long previous = 0; // timer to integrate the speed over time
unsigned long feedback = 0; // timer for feedback on the serial console
float x = 0, y = 0, a = 0; // absolute position (in meter) and angle (in radians)
float vx = 0, vy = 0, va = 0; // speed in meter per second, and angular speed in radians per second
float r1 = 0, r2 = 0, r3 = 0; // speed of the stepper motors, in steps per second
float target_x = 0, target_y = 0, target_a = 0;
unsigned long route_starttime = 4294967295;
unsigned long target_eta = 4294967295;
// when there is an active route, the speed is determined by the distance to the target waypoint
// when there is no route, the speed is determined manually by the user
int current_route = -1;
int current_segment = -1;
bool route_pause = false;
/********************************************************************************/
void startRoute(int route) {
unsigned long now = millis();
// stop the current movement
vx = 0;
vy = 0;
va = 0;
if (!config.absolute) {
// set the current position and orientation as zero
x = 0;
y = 0;
a = 0;
}
// read the waypoints from the SPIFFS filesystem
parseWaypoints(route);
// insert the current position and orientation as the starting point
waypoints_t.insert(waypoints_t.begin(), 0);
waypoints_x.insert(waypoints_x.begin(), x);
waypoints_y.insert(waypoints_y.begin(), y);
waypoints_a.insert(waypoints_a.begin(), a);
// start the new route
route_starttime = now;
current_route = route;
route_pause = false;
if (config.debug)
printWaypoints(route);
} // startRoute
/********************************************************************************/
void pauseRoute() {
// pause the current route
vx = 0;
vy = 0;
va = 0;
route_pause = true;
} // pauseRoute
/********************************************************************************/
void resumeRoute() {
// resume the current route
route_pause = false;
} // resumeRoute
/********************************************************************************/
void stopRoute() {
// stop the current route
vx = 0;
vy = 0;
va = 0;
current_route = -1;
route_starttime = 4294967295;
target_eta = 4294967295;
route_pause = false;
} // stopRoute
/********************************************************************************/
void resetRoute() {
// stop the current route and reset the current position to zero
stopRoute();
x = 0;
y = 0;
a = 0;
} // resetRoute
/********************************************************************************/
void updatePosition() {
unsigned long now = millis();
if (route_pause) {
// shift the time so that we remain in the same state
// this is as if the time has stopped
route_starttime += (now - previous);
target_eta += (now - previous);
}
else {
// integrate the speed over time to keep track of the current position and angle
x += vx * (now - previous) / 1000;
y += vy * (now - previous) / 1000;
a += va * (now - previous) / 1000;
}
previous = now;
} // updatePosition
/********************************************************************************/
void updateTarget() {
unsigned long now = millis();
if (current_route < 0) {
// when there is no route, the speed is determined manually by the user
// hence the current position is as good a target as anywhere else
target_x = x;
target_y = y;
target_a = a;
target_eta = 4294967295;
}
else {
// when there is an active route, the speed is determined by the distance to the target waypoint
// determine the segment or leg that we are currently on
current_segment = -1;
int n = waypoints_t.size();
unsigned long route_endtime = route_starttime + 1000 * waypoints_t.at(n - 1);
if (now < route_starttime) {
// the route has not yet started
// stay where we are
target_x = x;
target_y = y;
target_a = a;
target_eta = 4294967295;
}
else if (now >= route_endtime) {
// the route has finished
if (config.repeat) {
// start the same route again
startRoute(current_route);
}
else {
// stop the route
current_route = -1;
vx = 0;
vy = 0;
va = 0;
// stay where we are
target_x = x;
target_y = y;
target_a = a;
target_eta = 4294967295;
}
}
else {
// determine the segment or leg that we are currently on
// the N waypoints are connected by N-1 segments
for (int i = 0; i < (n - 1); i++) {
unsigned long segment_starttime = route_starttime + 1000 * waypoints_t.at(i);
unsigned long segment_endtime = route_starttime + 1000 * waypoints_t.at(i + 1);
if (now >= segment_starttime && now < segment_endtime) {
current_segment = i + 1;
target_x = waypoints_x.at(i + 1);
target_y = waypoints_y.at(i + 1);
target_a = waypoints_a.at(i + 1);
target_eta = segment_endtime;
break;
}
} // for all segments
} // if route_starttime
} // if current_route
} // updateTarget
/********************************************************************************/
void updateSpeed() {
unsigned long now = millis();
if (current_route < 0) {
// when there is no route, the speed is determined manually by the user
}
else if (route_pause) {
// don't move if the route is paused
vx = 0;
vy = 0;
va = 0;
}
else {
// when there is an active route, the speed is determined by the distance to the target waypoint
if (current_segment > 0) {
// determine the speed needed to reach the target at the desired time
float dt = 0.001 * (target_eta - now); // in seconds
if (dt > 0) {
vx = (target_x - x) / dt;
vy = (target_y - y) / dt;
va = (target_a - a) / dt;
}
else {
// infinite or negative speed is not allowed
vx = 0;
vy = 0;
va = 0;
}
}
else {
// the current segment could not be determined
vx = 0;
vy = 0;
va = 0;
} // if current_segment
} // if current_route
} // updateSpeed
/********************************************************************************/
void updateWheels() {
// convert the speed from world-coordinates into the rotation speed of the motors
r1 = sin(a ) * vx / wc - cos(a ) * vy / wc - va * (pc / wc) / (M_PI * 2);
r2 = sin(a + M_PI * 2 / 3) * vx / wc - cos(a + M_PI * 2 / 3) * vy / wc - va * (pc / wc) / (M_PI * 2);
r3 = sin(a - M_PI * 2 / 3) * vx / wc - cos(a - M_PI * 2 / 3) * vy / wc - va * (pc / wc) / (M_PI * 2);
// convert from rotations per second into steps per second
r1 *= totalsteps;
r2 *= totalsteps;
r3 *= totalsteps;
// the stepper motors cannot rotate at more than ~512 steps/second
float r = maxOfThree(abs(r1), abs(r2), abs(r3));
if (r > maxsteps) {
float reduction = r / maxsteps;
Serial.print("reducing speed by factor ");
Serial.println(reduction);
vx /= reduction;
vy /= reduction;
va /= reduction;
r1 /= reduction;
r2 /= reduction;
r3 /= reduction;
}
if (r1 == 0 && r2 == 0 && r3 == 0) {
// save energy by switching the motors off
wheel1.powerOff();
wheel2.powerOff();
wheel3.powerOff();
ledOn();
}
else {
wheel1.powerOn();
wheel2.powerOn();
wheel3.powerOn();
ledSlow();
}
// set the motor speed in steps per seconds
wheel1.spin(r1);
wheel2.spin(r2);
wheel3.spin(r3);
} // updateWheels
/********************************************************************************/
void printDebug() {
unsigned long now = millis();
// do not print more often than once every 500ms
if ((now - feedback) > 500) {
Serial.print("current = ");
Serial.print(current_route);
Serial.print(", ");
Serial.print(current_segment);
Serial.print(", time = ");
Serial.print(now);
Serial.print(", ");
Serial.print(target_eta);
Serial.print(", position = ");
Serial.print(x);
Serial.print(", ");
Serial.print(y);
Serial.print(", ");
Serial.print(a * 180 / M_PI); // in degrees
Serial.print(", target = ");
Serial.print(target_x);
Serial.print(", ");
Serial.print(target_y);
Serial.print(", ");
Serial.print(target_a * 180 / M_PI); // in degrees
Serial.print(", speed = ");
Serial.print(vx);
Serial.print(", ");
Serial.print(vy);
Serial.print(", ");
Serial.print(va * 180 / M_PI); // in degrees per second
Serial.print(", wheel speed = ");
Serial.print(r1);
Serial.print(", ");
Serial.print(r2);
Serial.print(", ");
Serial.print(r3);
Serial.println();
feedback = now;
}
}
/********************************************************************************/
void setup() {
Serial.begin(115200);
Serial.print("[ ");
Serial.print(host);
Serial.print(" / ");
Serial.print(version);
Serial.println(" ]");
ledInit();
ledFast();
// The SPIFFS file system contains the html and javascript code for the web interface, and the "config.json" file
if (!SPIFFS.begin()) {
Serial.println("Error opening file system");
while (true);
}
else {
Serial.println("Opened file system");
loadConfig();
printConfig();
}
WiFi.hostname(host);
WiFi.begin();
printMacAddress();
WiFiManager wifiManager;
wifiManager.setAPStaticIPConfig(IPAddress(192, 168, 1, 1), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));
wifiManager.autoConnect(host);
Serial.println("Connected to WiFi");
// incoming port for OSC messages
Udp.begin(inPort);
// this serves all URIs that can be resolved to a file on the SPIFFS filesystem
server.onNotFound(handleNotFound);
server.on("/", HTTP_GET, []() {
handleRedirect("/index.html");
});
server.on("/defaults", HTTP_GET, []() {
Serial.println("handleDefaults");
handleStaticFile("/reload_success.html");
defaultConfig();
printConfig();
saveConfig();
server.close();
server.stop();
ESP.restart();
});
server.on("/reconnect", HTTP_GET, []() {
Serial.println("handleReconnect");
handleStaticFile("/reload_success.html");
server.close();
server.stop();
delay(5000);
WiFiManager wifiManager;
wifiManager.resetSettings();
wifiManager.setAPStaticIPConfig(IPAddress(192, 168, 1, 1), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));
wifiManager.startConfigPortal(host);
server.begin();
if (WiFi.status() == WL_CONNECTED)
Serial.println("WiFi status ok");
});
server.on("/restart", HTTP_GET, []() {
Serial.println("handleRestart");
handleStaticFile("/reload_success.html");
server.close();
server.stop();
SPIFFS.end();
delay(5000);
ESP.restart();
});
server.on("/json", HTTP_PUT, handleJSON);
server.on("/json", HTTP_POST, handleJSON);
server.on("/json", HTTP_GET, [] {
JsonDocument root;
N_CONFIG_TO_JSON(repeat, "repeat");
N_CONFIG_TO_JSON(absolute, "absolute");
N_CONFIG_TO_JSON(warp, "warp");
N_CONFIG_TO_JSON(debug, "debug");
root["version"] = version;
root["uptime"] = long(millis() / 1000);
root["macaddress"] = getMacAddress();
root["waypoints1"] = loadWaypoints(1);
root["waypoints2"] = loadWaypoints(2);
root["waypoints3"] = loadWaypoints(3);
root["waypoints4"] = loadWaypoints(4);
root["waypoints5"] = loadWaypoints(5);
root["waypoints6"] = loadWaypoints(6);
root["waypoints7"] = loadWaypoints(7);
root["waypoints8"] = loadWaypoints(8);
String str;
serializeJson(root, str);
server.setContentLength(str.length());
server.send(200, "application/json", str);
});
// start the web server
server.begin();
// announce the hostname and web server through zeroconf
MDNS.begin(host);
MDNS.addService("http", "tcp", 80);
// connect the timer to the stepper motor driver pins
wheel1.begin(14, 27, 26, 25);
wheel2.begin(13, 15, 2, 4);
wheel3.begin(16, 17, 5, 18);
Serial.println("Setup done");
} // setup
/********************************************************************************/
void loop() {
updatePosition(); // update the current location
updateTarget(); // update the target location
updateSpeed(); // update the world speed
updateWheels(); // update the speed of the wheels
if (config.debug)
printDebug();
parseOSC(); // parse the incoming OSC messages
server.handleClient(); // handle webserver requests, note that this may disrupt other processes
} // loop
================================================
FILE: esp32_3wd_stepper/parseosc.cpp
================================================
#include "parseosc.h"
/********************************************************************************/
void parseOSC() {
int size = Udp.parsePacket();
if (size > 0) {
OSCMessage msg;
OSCBundle bundle;
OSCErrorCode error;
char c = Udp.peek();
if (c == '#') {
// it is a bundle, these are sent by TouchDesigner
while (size--) {
bundle.fill(Udp.read());
}
if (!bundle.hasError()) {
bundle.dispatch("/*", printCallback) || bundle.dispatch("/*/*", printCallback) || bundle.dispatch("/*/*/*", printCallback);
bundle.dispatch("/route/1", route1Callback);
bundle.dispatch("/route/2", route2Callback);
bundle.dispatch("/route/3", route3Callback);
bundle.dispatch("/route/4", route4Callback);
bundle.dispatch("/route/5", route5Callback);
bundle.dispatch("/route/6", route6Callback);
bundle.dispatch("/route/7", route7Callback);
bundle.dispatch("/route/8", route8Callback);
bundle.dispatch("/pause", pauseCallback);
bundle.dispatch("/resume", resumeCallback);
bundle.dispatch("/stop", stopCallback);
bundle.dispatch("/reset", resetCallback);
bundle.dispatch("/adjust/x", dxCallback);
bundle.dispatch("/adjust/y", dyCallback);
bundle.dispatch("/adjust/a", daCallback);
bundle.dispatch("/manual/x", xCallback);
bundle.dispatch("/manual/y", yCallback);
bundle.dispatch("/manual/a", aCallback);
bundle.dispatch("/manual/xy/1", xyCallback); // this is for the 2D panel in touchOSC
bundle.dispatch("/accxyz", accxyzCallback); // this is for the accelerometer in touchOSC
} else {
error = bundle.getError();
Serial.print("error: ");
Serial.println(error);
}
} else if (c == '/') {
// it is a message, these are sent by touchOSC
while (size--) {
msg.fill(Udp.read());
}
if (!msg.hasError()) {
msg.dispatch("/*", printCallback) || msg.dispatch("/*/*", printCallback) || msg.dispatch("/*/*/*", printCallback);
msg.dispatch("/route/1", route1Callback);
msg.dispatch("/route/2", route2Callback);
msg.dispatch("/route/3", route3Callback);
msg.dispatch("/route/4", route4Callback);
msg.dispatch("/route/5", route5Callback);
msg.dispatch("/route/6", route6Callback);
msg.dispatch("/route/7", route7Callback);
msg.dispatch("/route/8", route8Callback);
msg.dispatch("/pause", pauseCallback);
msg.dispatch("/resume", resumeCallback);
msg.dispatch("/stop", stopCallback);
msg.dispatch("/reset", resetCallback);
msg.dispatch("/adjust/x", dxCallback);
msg.dispatch("/adjust/y", dyCallback);
msg.dispatch("/adjust/a", daCallback);
msg.dispatch("/manual/x", xCallback);
msg.dispatch("/manual/y", yCallback);
msg.dispatch("/manual/a", aCallback);
msg.dispatch("/manual/xy/1", xyCallback); // this is for the 2D panel in touchOSC
msg.dispatch("/accxyz", accxyzCallback); // this is for the accelerometer in touchOSC
} else {
error = msg.getError();
Serial.print("error: ");
Serial.println(error);
}
}
} // if size
} // parseOSC
/********************************************************************************/
void printCallback(OSCMessage &msg) {
Serial.print(msg.getAddress());
Serial.print(" : ");
for (int i = 0; i < msg.size(); i++) {
if (msg.isInt(i)) {
Serial.print(msg.getInt(i));
} else if (msg.isFloat(i)) {
Serial.print(msg.getFloat(i));
} else if (msg.isDouble(i)) {
Serial.print(msg.getDouble(i));
} else if (msg.isBoolean(i)) {
Serial.print(msg.getBoolean(i));
} else if (msg.isString(i)) {
char buffer[256];
msg.getString(i, buffer);
Serial.print(buffer);
} else {
Serial.print("?");
}
if (i < (msg.size() - 1)) {
Serial.print(", "); // there are more to come
}
}
Serial.println();
}
/********************************************************************************/
void route1Callback(OSCMessage &msg) {
if (msg.getInt(0))
startRoute(1);
}
void route2Callback(OSCMessage &msg) {
if (msg.getInt(0))
startRoute(2);
}
void route3Callback(OSCMessage &msg) {
if (msg.getInt(0))
startRoute(3);
}
void route4Callback(OSCMessage &msg) {
if (msg.getInt(0))
startRoute(4);
}
void route5Callback(OSCMessage &msg) {
if (msg.getInt(0))
startRoute(5);
}
void route6Callback(OSCMessage &msg) {
if (msg.getInt(0))
startRoute(6);
}
void route7Callback(OSCMessage &msg) {
if (msg.getInt(0))
startRoute(7);
}
void route8Callback(OSCMessage &msg) {
if (msg.getInt(0))
startRoute(8);
}
void pauseCallback(OSCMessage &msg) {
if (msg.getInt(0))
pauseRoute();
}
void resumeCallback(OSCMessage &msg) {
if (msg.getInt(0))
resumeRoute();
}
void stopCallback(OSCMessage &msg) {
if (msg.getInt(0))
stopRoute();
}
void resetCallback(OSCMessage & msg) {
if (msg.getInt(0))
resetRoute();
}
void xCallback(OSCMessage & msg) {
vx = msg.getFloat(0);
}
void yCallback(OSCMessage & msg) {
vy = msg.getFloat(0);
}
void aCallback(OSCMessage & msg) {
va = msg.getFloat(0);
}
void dxCallback(OSCMessage & msg) {
// adjust the current position with 1 cm
// the negative adjustment will cause the robot to move towards the positive side
x -= msg.getFloat(0) * 0.01;
}
void dyCallback(OSCMessage & msg) {
// adjust the current position with 1 cm
// the negative adjustment will cause the robot to move towards the positive side
y -= msg.getFloat(0) * 0.01;
}
void daCallback(OSCMessage & msg) {
// adjust the current angle with 5 degrees (in radians)
// the negative adjustment will cause the robot to move towards a positive angle
a -= msg.getFloat(0) * 5.00 * PI / 180;
}
void xyCallback(OSCMessage & msg) {
vx = msg.getFloat(0);
vy = msg.getFloat(1);
}
void accxyzCallback(OSCMessage & msg) {
// values are between -1 and 1, scale them to a more appropriate speed of 3 cm/s
vx = msg.getFloat(0) * 0.03;
vy = msg.getFloat(1) * 0.03;
}
================================================
FILE: esp32_3wd_stepper/parseosc.h
================================================
#ifndef _PARSEOSC_H_
#define _PARSEOSC_H_
#include <WiFiUdp.h>
#include <OSCMessage.h> // https://github.com/CNMAT/OSC
#include <OSCBundle.h>
// these are defined in the main sketch
extern float vx, vy, va;
extern float x, y, a;
extern WiFiUDP Udp;
void startRoute(int);
void pauseRoute();
void resumeRoute();
void stopRoute();
void resetRoute();
// these are defined in the corresponding CPP file
void parseOSC();
void printCallback(OSCMessage &);
void route1Callback(OSCMessage &);
void route2Callback(OSCMessage &);
void route3Callback(OSCMessage &);
void route4Callback(OSCMessage &);
void route5Callback(OSCMessage &);
void route6Callback(OSCMessage &);
void route7Callback(OSCMessage &);
void route8Callback(OSCMessage &);
void pauseCallback(OSCMessage &);
void resumeCallback(OSCMessage &);
void stopCallback(OSCMessage &);
void resetCallback(OSCMessage &);
void xCallback(OSCMessage &);
void yCallback(OSCMessage &);
void aCallback(OSCMessage &);
void dxCallback(OSCMessage &);
void dyCallback(OSCMessage &);
void daCallback(OSCMessage &);
void xyCallback(OSCMessage &);
void accxyzCallback(OSCMessage &);
#endif
================================================
FILE: esp32_3wd_stepper/stepper.cpp
================================================
#include "stepper.h"
// This implements the ESP32 control for the 28BYJ-48 motor and the ULN2003 driver board.
//
// It is specifically written to keep up to four stepper motors spinning at a constant speed
// and it uses ESP32 hardware timers for precise timing.
//
// The https://docs.arduino.cc/libraries/stepper/ was not convenient to set and
// keep the stepper motor runnning at a specific speed.
//
// The https://github.com/bblanchon/ArduinoContinuousStepper library was not
// precise enough to simultaneously control three stepper motors and to keep
// my 3-wheel robot platform with omni wheels going straight.
//
// See https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/timer.html
/******************************************************************************/
Stepper::Stepper() {
// constructor
}
/******************************************************************************/
void Stepper::begin(unsigned int _in1, int _in2, int _in3, int _in4) {
in1 = _in1;
in2 = _in2;
in3 = _in3;
in4 = _in4;
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);
timer = timerBegin(frequency);
timerAttachInterruptArg(timer, &onTimer, (void*)this);
}
/******************************************************************************/
void Stepper::spin(int speed) {
if (speed > 0) {
direction = +1;
timerAlarm(timer, frequency / (+speed * 2), true, 0); // speed times two since half-steps
}
else if (speed < 0) {
direction = -1;
timerAlarm(timer, frequency / (-speed * 2), true, 0); // speed times two since half-steps
}
else if (speed == 0) {
direction = 0;
timerAlarm(timer, 0, false, 0); // execute the alarm once, do not repeat
}
}
/******************************************************************************/
void Stepper::powerOn() {
power = true;
}
void Stepper::powerOff() {
power = false;
}
/******************************************************************************/
void IRAM_ATTR Stepper::onTimer(void* arg) {
// This is to work around the error that ISO C++ forbids taking the address of
// an unqualified or parenthesized non-static member function to form a pointer
// to member function.
Stepper* pObj = (Stepper*) arg;
pObj->doStep();
}
/******************************************************************************/
void IRAM_ATTR Stepper::doStep() {
if (power) {
digitalWrite(in1, bitRead(halfstep[step], 0));
digitalWrite(in2, bitRead(halfstep[step], 1));
digitalWrite(in3, bitRead(halfstep[step], 2));
digitalWrite(in4, bitRead(halfstep[step], 3));
// increment or decrement, depending on the direction
step += direction;
// wrap between 0 and 7
if (step < 0)
step = 7;
else if (step > 7)
step = 0;
}
else {
digitalWrite(in1, 0);
digitalWrite(in2, 0);
digitalWrite(in3, 0);
digitalWrite(in4, 0);
}
}
================================================
FILE: esp32_3wd_stepper/stepper.h
================================================
#ifndef _STEPPER_H_
#define _STEPPER_H_
#include <Arduino.h>
class Stepper {
public:
Stepper();
void begin(unsigned int in1, int in2, int in3, int in4);
void spin(int speed);
void powerOn();
void powerOff();
private:
unsigned int frequency = 10000000;
unsigned int halfstep[8] = {0b1000, 0b1100, 0b0100, 0b0110, 0b0010, 0b0011, 0b0001, 0b1001}; // half-step drive
unsigned int in1, in2, in3, in4; // the ESP32 pins connected to the ULN2003 driver board
int step = 0; // from 0 to 7, wraps around
int direction = 1; // +1 or -1
bool power = true; // true or false
hw_timer_t *timer = NULL;
static void IRAM_ATTR onTimer(void* arg); // this calls doStep
void IRAM_ATTR doStep(); // this implements the actual stepping
};
#endif // _STEPPER_H_
================================================
FILE: esp32_3wd_stepper/util.cpp
================================================
#include "util.h"
/********************************************************************************/
void printMacAddress() {
uint8_t baseMac[6];
esp_err_t ret = esp_wifi_get_mac(WIFI_IF_STA, baseMac);
if (ret == ESP_OK) {
Serial.printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
baseMac[0], baseMac[1], baseMac[2],
baseMac[3], baseMac[4], baseMac[5]);
} else {
Serial.println("Failed to read MAC address");
}
}
/********************************************************************************/
String getMacAddress() {
uint8_t baseMac[6];
String s;
esp_err_t ret = esp_wifi_get_mac(WIFI_IF_STA, baseMac);
if (ret != ESP_OK) {
s += "unknown";
}
else {
for (byte i = 0; i < 6; ++i) {
char buf[3];
sprintf(buf, "%02X", baseMac[i]);
s += buf;
if (i < 5) s += ':';
}
}
return s;
}
/********************************************************************************/
float maxOfThree(float a, float b, float c) {
if (a >= b && a >= c) {
return a;
}
else if (b >= a && b >= c) {
return b;
}
else {
return c;
}
}
================================================
FILE: esp32_3wd_stepper/util.h
================================================
#ifndef _UTIL_H_
#define _UTIL_H_
#include <Arduino.h>
#include <WiFi.h>
#include <esp_wifi.h>
void printMacAddress(void);
String getMacAddress(void);
float maxOfThree(float, float, float);
#endif // _UTIL_H_
================================================
FILE: esp32_3wd_stepper/waypoints.cpp
================================================
#include "waypoints.h"
// read the sequence of waypoints from the CSV file on SPIFFS
//
// the file should have no header
// each line should consist of four comma-separated values: time, x, y, theta
// time is incremental and in seconds
// x and y are the position in meter
// theta is the angle in degrees in the file, it is converted to radians in memory
vector<float> waypoints_t;
vector<float> waypoints_x;
vector<float> waypoints_y;
vector<float> waypoints_a;
/****************************************************************************************/
void printWaypoints(int route) {
Serial.print("printWaypoints ");
Serial.println(route);
for (int i = 0; i < waypoints_t.size(); i++) {
Serial.print(waypoints_t.at(i));
Serial.print(",");
Serial.print(waypoints_x.at(i));
Serial.print(",");
Serial.print(waypoints_y.at(i));
Serial.print(",");
Serial.print(waypoints_a.at(i));
Serial.print("\n");
}
return;
}
/****************************************************************************************/
size_t saveWaypoints(int route, String s) {
Serial.print("saveWaypoints ");
Serial.println(route);
if (!SPIFFS.begin(true)) {
Serial.println("An error has occurred while mounting SPIFFS");
return 0;
}
String filename = String("/waypoints") + String(route) + String(".csv");
File file = SPIFFS.open(filename, "w");
if (!file) {
Serial.print("Failed to open ");
Serial.print(filename);
Serial.println(" for writing");
}
size_t bytes = file.print(s);
return bytes;
}
/****************************************************************************************/
String loadWaypoints(int route) {
Serial.print("loadWaypoints ");
Serial.println(route);
if (!SPIFFS.begin(true)) {
Serial.println("An error has occurred while mounting SPIFFS");
String s = "";
return s;
}
String filename = String("/waypoints") + String(route) + String(".csv");
File file = SPIFFS.open(filename, "r");
if (!file) {
Serial.print("Failed to open ");
Serial.print(filename);
Serial.println(" for reading");
}
String s;
while (file.available())
{
char charRead = file.read();
s += charRead;
}
return s;
}
/****************************************************************************************/
void parseWaypoints(int route) {
Serial.print("parseWaypoints ");
Serial.println(route);
if (!SPIFFS.begin(true)) {
Serial.println("An error has occurred while mounting SPIFFS");
return;
}
String filename = String("/waypoints") + String(route) + String(".csv");
File file = SPIFFS.open(filename, "r");
if (!file) {
Serial.print("Failed to open ");
Serial.print(filename);
Serial.println(" for reading");
}
waypoints_t.clear();
waypoints_x.clear();
waypoints_y.clear();
waypoints_a.clear();
vector<String> v;
while (file.available()) {
String s = file.readStringUntil('\n');
v.push_back(s);
int ind1 = s.indexOf(','); // finds location of 1st ,
int ind2 = s.indexOf(',', ind1 + 1); // finds location of 2nd ,
int ind3 = s.indexOf(',', ind2 + 1); // finds location of 3rd ,
int ind4 = s.length(); // end of the string
String tok1 = s.substring(0, ind1); //captures 1st value
String tok2 = s.substring(ind1 + 1, ind2); //captures 2nd value
String tok3 = s.substring(ind2 + 1, ind3); //captures 3rd value
String tok4 = s.substring(ind3 + 1, ind4); //captures 4th value
// convert the values to float and store each in their own vector
waypoints_t.push_back(atof(tok1.c_str()));
waypoints_x.push_back(atof(tok2.c_str()));
waypoints_y.push_back(atof(tok3.c_str()));
waypoints_a.push_back(atof(tok4.c_str()) * M_PI / 180); // convert from degrees to radians
}
file.close();
return;
}
================================================
FILE: esp32_3wd_stepper/waypoints.h
================================================
#ifndef _WAYPOINTS_H_
#define _WAYPOINTS_H_
#include <FS.h>
#include <SPIFFS.h>
#include <vector>
using namespace std;
extern vector<float> waypoints_t;
extern vector<float> waypoints_x;
extern vector<float> waypoints_y;
extern vector<float> waypoints_a;
// the following functions take an integer as first input, which is the route number
// the route number is between 1 to 8, so you can use 8 predefined sets of waypoints
void printWaypoints(int); // print the waypoints on the serial console
void parseWaypoints(int); // read the waypoints from the file and represent as vectors
String loadWaypoints(int); // read the waypoints from the file and return as a string
size_t saveWaypoints(int, String); // save the waypoints (as string) to the file
#endif // _WAYPOINTS_H_
================================================
FILE: esp32_3wd_stepper/webinterface.cpp
================================================
#include "webinterface.h"
#include "waypoints.h"
Config config;
extern WebServer server;
/***************************************************************************/
static String getContentType(const String& path) {
if (path.endsWith(".html")) return "text/html";
else if (path.endsWith(".htm")) return "text/html";
else if (path.endsWith(".css")) return "text/css";
else if (path.endsWith(".txt")) return "text/plain";
else if (path.endsWith(".js")) return "application/javascript";
else if (path.endsWith(".png")) return "image/png";
else if (path.endsWith(".gif")) return "image/gif";
else if (path.endsWith(".jpg")) return "image/jpeg";
else if (path.endsWith(".jpeg")) return "image/jpeg";
else if (path.endsWith(".ico")) return "image/x-icon";
else if (path.endsWith(".svg")) return "image/svg+xml";
else if (path.endsWith(".xml")) return "text/xml";
else if (path.endsWith(".pdf")) return "application/pdf";
else if (path.endsWith(".zip")) return "application/zip";
else if (path.endsWith(".gz")) return "application/x-gzip";
else if (path.endsWith(".json")) return "application/json";
return "application/octet-stream";
}
/***************************************************************************/
bool defaultConfig() {
Serial.println("defaultConfig");
config.repeat = 0;
config.absolute = 0;
config.warp = 1.000;
config.debug = 0;
return true;
}
bool loadConfig() {
Serial.println("loadConfig");
File configFile = SPIFFS.open("/config.json", "r");
if (!configFile) {
Serial.println("Failed to open config file");
return false;
}
size_t size = configFile.size();
if (size > 1024) {
Serial.println("Config file size is too large");
return false;
}
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
configFile.close();
JsonDocument root;
DeserializationError error = deserializeJson(root, buf.get());
if (error) {
Serial.println("Failed to parse config file");
return false;
}
N_JSON_TO_CONFIG(repeat, "repeat");
N_JSON_TO_CONFIG(absolute, "absolute");
N_JSON_TO_CONFIG(warp, "warp");
N_JSON_TO_CONFIG(debug, "debug");
return true;
}
bool saveConfig() {
Serial.println("saveConfig");
JsonDocument root;
N_CONFIG_TO_JSON(repeat, "repeat");
N_CONFIG_TO_JSON(absolute, "absolute");
N_CONFIG_TO_JSON(warp, "warp");
N_CONFIG_TO_JSON(debug, "debug");
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
Serial.println("Failed to open config file for writing");
return false;
} else {
Serial.println("Writing to config file");
serializeJson(root, configFile);
configFile.close();
return true;
}
}
void printConfig() {
Serial.print("repeat = ");
Serial.println(config.repeat);
Serial.print("absolute = ");
Serial.println(config.absolute);
Serial.print("warp = ");
Serial.println(config.warp);
Serial.print("debug = ");
Serial.println(config.debug);
}
void printRequest() {
String message = "HTTP Request\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nHeaders: ";
message += server.headers();
message += "\n";
for (uint8_t i = 0; i < server.headers(); i++) {
message += " " + server.headerName(i) + ": " + server.header(i) + "\n";
}
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
Serial.println(message);
}
void handleNotFound() {
Serial.print("handleNotFound: ");
Serial.println(server.uri());
if (SPIFFS.exists(server.uri())) {
handleStaticFile(server.uri());
} else {
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.setContentLength(message.length());
server.send(404, "text/plain", message);
}
}
void handleRedirect(const char* filename) {
handleRedirect((String)filename);
}
void handleRedirect(String filename) {
Serial.println("handleRedirect: " + filename);
server.sendHeader("Location", filename, true);
server.setContentLength(0);
server.send(302, "text/plain", "");
}
bool handleStaticFile(const char* path) {
return handleStaticFile((String)path);
}
bool handleStaticFile(String path) {
Serial.println("handleStaticFile: " + path);
String contentType = getContentType(path); // Get the MIME type
if (SPIFFS.exists(path)) { // If the file exists
File file = SPIFFS.open(path, "r"); // Open it
server.setContentLength(file.size());
server.streamFile(file, contentType); // And send it to the client
file.close(); // Then close the file again
return true;
}
Serial.println("\tFile Not Found");
return false; // If the file doesn't exist, return false
}
void handleJSON() {
// this gets called in response to either a PUT or a POST
Serial.println("handleJSON");
printRequest();
if (server.hasArg("repeat") || server.hasArg("absolute") || server.hasArg("warp") || server.hasArg("debug")
|| server.hasArg("waypoints1")
|| server.hasArg("waypoints2")
|| server.hasArg("waypoints3")
|| server.hasArg("waypoints4")
|| server.hasArg("waypoints5")
|| server.hasArg("waypoints6")
|| server.hasArg("waypoints7")
|| server.hasArg("waypoints8")) {
// the body is key1=val1&key2=val2&key3=val3 and the ESP8266Webserver has already parsed it
N_KEYVAL_TO_CONFIG(repeat, "repeat");
N_KEYVAL_TO_CONFIG(absolute, "absolute");
N_KEYVAL_TO_CONFIG(warp, "warp");
N_KEYVAL_TO_CONFIG(debug, "debug");
if (server.hasArg("waypoints1"))
saveWaypoints(1, server.arg("waypoints1"));
if (server.hasArg("waypoints2"))
saveWaypoints(2, server.arg("waypoints2"));
if (server.hasArg("waypoints3"))
saveWaypoints(3, server.arg("waypoints3"));
if (server.hasArg("waypoints4"))
saveWaypoints(4, server.arg("waypoints4"));
if (server.hasArg("waypoints5"))
saveWaypoints(5, server.arg("waypoints5"));
if (server.hasArg("waypoints6"))
saveWaypoints(6, server.arg("waypoints6"));
if (server.hasArg("waypoints7"))
saveWaypoints(7, server.arg("waypoints7"));
if (server.hasArg("waypoints8"))
saveWaypoints(8, server.arg("waypoints8"));
handleStaticFile("/reload_success.html");
} else if (server.hasArg("plain")) {
// parse the body as JSON object
JsonDocument root;
DeserializationError error = deserializeJson(root, server.arg("plain"));
if (error) {
Serial.println("either here");
handleStaticFile("/reload_failure.html");
return;
}
N_JSON_TO_CONFIG(repeat, "repeat");
N_JSON_TO_CONFIG(absolute, "absolute");
N_JSON_TO_CONFIG(warp, "warp");
N_JSON_TO_CONFIG(debug, "debug");
if (root.containsKey("waypoints1"))
saveWaypoints(1, root["waypoints1"]);
if (root.containsKey("waypoints2"))
saveWaypoints(2, root["waypoints2"]);
if (root.containsKey("waypoints3"))
saveWaypoints(3, root["waypoints3"]);
if (root.containsKey("waypoints4"))
saveWaypoints(4, root["waypoints4"]);
if (root.containsKey("waypoints5"))
saveWaypoints(5, root["waypoints5"]);
if (root.containsKey("waypoints6"))
saveWaypoints(6, root["waypoints6"]);
if (root.containsKey("waypoints7"))
saveWaypoints(7, root["waypoints7"]);
if (root.containsKey("waypoints8"))
saveWaypoints(8, root["waypoints8"]);
handleStaticFile("/reload_success.html");
} else {
handleStaticFile("/reload_failure.html");
return; // do not save the configuration
}
saveConfig();
printConfig();
}
================================================
FILE: esp32_3wd_stepper/webinterface.h
================================================
#ifndef _WEBINTERFACE_H_
#define _WEBINTERFACE_H_
#include <Arduino.h>
#include <WebServer.h>
#include <ArduinoJson.h> // https://arduinojson.org
#include <FS.h>
#include <SPIFFS.h>
#ifndef ARDUINOJSON_VERSION
#error ArduinoJson version 7 not found, please include ArduinoJson.h in your .ino file
#endif
#if ARDUINOJSON_VERSION_MAJOR != 7
#error ArduinoJson version 7 is required
#endif
/* these are for numbers */
#define N_JSON_TO_CONFIG(x, y) { if (root.containsKey(y)) { config.x = root[y]; } }
#define N_CONFIG_TO_JSON(x, y) { root[y] = config.x; }
#define N_KEYVAL_TO_CONFIG(x, y) { if (server.hasArg(y)) { String str = server.arg(y); config.x = str.toFloat(); } }
/* these are for strings */
#define S_JSON_TO_CONFIG(x, y) { if (root.containsKey(y)) { strcpy(config.x, root[y]); } }
#define S_CONFIG_TO_JSON(x, y) { root.set(y, config.x); }
#define S_KEYVAL_TO_CONFIG(x, y) { if (server.hasArg(y)) { String str = server.arg(y); strcpy(config.x, str.c_str()); } }
struct Config {
int repeat;
int absolute;
float warp;
int debug;
};
extern Config config;
bool defaultConfig(void);
bool loadConfig(void);
bool saveConfig(void);
void printConfig(void);
void handleNotFound(void);
void handleRedirect(String);
void handleRedirect(const char *);
bool handleStaticFile(String);
bool handleStaticFile(const char *);
void handleJSON();
#endif // _WEBINTERFACE_H_
================================================
FILE: esp32_config_webinterface_v5/README.md
================================================
# Overview
This is a demonstration and test sketch for configuring persistent options via the web interface. This strategy is used in a number of my functional ESP32 and ESP8266 sketches. It consists of a `settings.json` file in the SPIFFS filesystem and a `settings.html` file with some javascript.
**This specific version is for ArduinoJson 5, there are other examples for version 6 and 7.**
The settings can be changed in the webbrowser at http://192.168.1.xxx/settings and clicking "Send". This results in `Webserver` parsing the arguments and placing the variables in the header, but also places the query string in the body.
The following results in `Webserver` parsing the arguments and placing the variables in the header.
curl -X POST 'http://192.168.1.xxx/json?var1=11&var2=22&var3=33'
curl -X PUT 'http://192.168.1.xxx/json?var1=11&var2=22&var3=33'
The following is not parsed, but only places the JSON data in the body.
curl -X POST http://192.168.1.xxx/json -d '{"var1":11,"var2":22,"var3":33}'
curl -X PUT http://192.168.1.xxx/json -d '{"var1":11,"var2":22,"var3":33}'
## SPIFFS for static files
You should not only write the firmware to the ESP32 module, but also the static content for the web interface. The html, css and javascript files located in the data directory should be written to the SPIFS filesystem on the ESP32. See https://github.com/me-no-dev/arduino-esp32fs-plugin.
You will get a "file not found" error if the firmware cannot access the data files.
## Arduino ESP32 filesystem uploader
This Arduino sketch includes a `data` directory with a number of files that should be uploaded to the ESP32 using the [SPIFFS filesystem uploader](https://github.com/me-no-dev/arduino-esp32fs-plugin) tool. At the moment (Feb 2024) the Arduino 2.x IDE does *not* support the SPIFFS filesystem uploader plugin. You have to use the Arduino 1.8.x IDE (recommended), or the command line utilities for uploading the data.
================================================
FILE: esp32_config_webinterface_v5/data/config.json
================================================
{
"var1":10,
"var2":20,
"var3":30,
"var4":40
}
================================================
FILE: esp32_config_webinterface_v5/data/hello.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Hello</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<p>Hello panda!</p>
<img src="panda.jpg">
</body>
</html>
================================================
FILE: esp32_config_webinterface_v5/data/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Index</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<a href="/hello.html">Hello</a></br>
<a href="/monitor.html">Monitor</a></br>
<a href="/settings.html">Change settings</a></br>
<a href="/reconnect">Reconnect WiFi</a></br>
<a href="/defaults">Default settings</a></br>
<a href="/restart">Restart hardware</a></br>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v5/data/monitor.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Monitor</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>Monitor</h1>
Firmware version:
<div id="version" name="version">?</div>
Uptime:
<div id="uptime" name"uptime">?</div>
<script>
async function updateContent() {
const response = await fetch("json");
const data = await response.json();
console.log(data);
document.getElementById("version").innerHTML = data["version"];
document.getElementById("uptime").innerHTML = data["uptime"];
}
updateContent();
</script>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v5/data/reload_failure.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Failure</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<p>Failure</p>
<script type="text/javascript">
setTimeout(function(){location="/index.html"},1000);
</script>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v5/data/reload_success.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Success</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<p>Success</p>
<script type="text/javascript">
setTimeout(function(){location="/index.html"},1000);
</script>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v5/data/settings.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Settings</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>Settings</h1>
<form id="settings-form" method="post" action="json">
<div class="field">
<label for="var1">var1:</label>
<input type="text" id="var1" name="var1" value="?" required>
</div>
<div class="field">
<label for="var2">var2:</label>
<input type="text" id="var2" name="var2" value="?" required>
</div>
<div class="field">
<label for="var2">var3:</label>
<input type="text" id="var3" name="var3" value="?" required>
</div>
<div class="field">
<button type="submit">Save</button>
</div>
</form>
<script>
async function updateContent() {
const response = await fetch("json");
const data = await response.json();
console.log(data);
document.getElementById("var1").value = data["var1"];
document.getElementById("var2").value = data["var2"];
document.getElementById("var3").value = data["var3"];
}
updateContent();
</script>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v5/data/style.css
================================================
.c{
text-align: center;
}
div,input{
padding:5px;font-size:1em;
}
input{
width:95%;
}
body{
text-align: center;font-family:verdana;
}
button{
border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;
}
.q{
float: right;width: 64px;text-align: right;
}
.l{
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAALVBMVEX///8EBwfBwsLw8PAzNjaCg4NTVVUjJiZDRUUUFxdiZGSho6OSk5Pg4eFydHTCjaf3AAAAZElEQVQ4je2NSw7AIAhEBamKn97/uMXEGBvozkWb9C2Zx4xzWykBhFAeYp9gkLyZE0zIMno9n4g19hmdY39scwqVkOXaxph0ZCXQcqxSpgQpONa59wkRDOL93eAXvimwlbPbwwVAegLS1HGfZAAAAABJRU5ErkJggg==") no-repeat left center;background-size: 1em;
}
================================================
FILE: esp32_config_webinterface_v5/esp32_config_webinterface_v5.ino
================================================
#if not defined(ESP32)
#error This is a sketch for an ESP32 board, like the NodeMCU 32S, LOLIN32 Lite, or the Adafruit Huzzah32
#endif
#include <Arduino.h>
#include <ArduinoJson.h> // https://arduinojson.org
#include <WiFi.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <FS.h>
#include <SPIFFS.h>
#ifndef ARDUINOJSON_VERSION
#error ArduinoJson version 5 not found, please include ArduinoJson.h in your .ino file
#endif
#if ARDUINOJSON_VERSION_MAJOR != 5
#error ArduinoJson version 5 is required
#endif
WebServer server(80);
const char* host = "ESP32";
const char* version = __DATE__ " / " __TIME__;
int var1, var2, var3;
static String getContentType(const String& path) {
if (path.endsWith(".html")) return "text/html";
else if (path.endsWith(".htm")) return "text/html";
else if (path.endsWith(".css")) return "text/css";
else if (path.endsWith(".txt")) return "text/plain";
else if (path.endsWith(".js")) return "application/javascript";
else if (path.endsWith(".png")) return "image/png";
else if (path.endsWith(".gif")) return "image/gif";
else if (path.endsWith(".jpg")) return "image/jpeg";
else if (path.endsWith(".jpeg")) return "image/jpeg";
else if (path.endsWith(".ico")) return "image/x-icon";
else if (path.endsWith(".svg")) return "image/svg+xml";
else if (path.endsWith(".xml")) return "text/xml";
else if (path.endsWith(".pdf")) return "application/pdf";
else if (path.endsWith(".zip")) return "application/zip";
else if (path.endsWith(".gz")) return "application/x-gzip";
else if (path.endsWith(".json")) return "application/json";
return "application/octet-stream";
}
bool defaultConfig() {
Serial.println("defaultConfig");
var1 = 11;
var2 = 22;
var3 = 33;
return true;
}
bool loadConfig() {
Serial.println("loadConfig");
File configFile = SPIFFS.open("/config.json", "r");
if (!configFile) {
Serial.println("Failed to open config file");
return false;
}
size_t size = configFile.size();
if (size > 1024) {
Serial.println("Config file size is too large");
return false;
}
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
StaticJsonBuffer<200> jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(buf.get());
if (!root.success()) {
Serial.println("Failed to parse config file");
return false;
}
if (root.containsKey("var1"))
var1 = root["var1"];
if (root.containsKey("var2"))
var2 = root["var2"];
if (root.containsKey("var3"))
var3 = root["var3"];
return true;
}
bool saveConfig() {
Serial.println("saveConfig");
printConfig();
StaticJsonBuffer<200> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root["var1"] = var1;
root["var2"] = var2;
root["var3"] = var3;
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
Serial.println("Failed to open config file for writing");
return false;
} else {
root.printTo(configFile);
return true;
}
}
void printConfig() {
Serial.print("var1 = ");
Serial.println(var1);
Serial.print("var2 = ");
Serial.println(var2);
Serial.print("var3 = ");
Serial.println(var3);
}
void printRequest() {
String message = "HTTP Request\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nHeaders: ";
message += server.headers();
message += "\n";
for (uint8_t i = 0; i < server.headers(); i++) {
message += " " + server.headerName(i) + ": " + server.header(i) + "\n";
}
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
Serial.println(message);
}
void handleNotFound() {
Serial.print("handleNotFound: ");
Serial.println(server.uri());
if (SPIFFS.exists(server.uri())) {
handleStaticFile(server.uri());
} else {
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.setContentLength(message.length());
server.send(404, "text/plain", message);
}
}
void handleRedirect(const char* filename) {
handleRedirect((String)filename);
}
void handleRedirect(String filename) {
Serial.println("handleRedirect: " + filename);
server.sendHeader("Location", filename, true);
server.setContentLength(0);
server.send(302, "text/plain", "");
}
bool handleStaticFile(const char* path) {
return handleStaticFile((String)path);
}
bool handleStaticFile(String path) {
Serial.println("handleStaticFile: " + path);
String contentType = getContentType(path); // Get the MIME type
if (SPIFFS.exists(path)) { // If the file exists
File file = SPIFFS.open(path, "r"); // Open it
server.setContentLength(file.size());
server.streamFile(file, contentType); // And send it to the client
file.close(); // Then close the file again
return true;
} else {
Serial.println("\tFile Not Found");
return false; // If the file doesn't exist, return false
}
}
void handleJSON() {
// this gets called in response to either a PUT or a POST
Serial.println("handleJSON");
printRequest();
if (server.hasArg("var1") || server.hasArg("var2") || server.hasArg("var3")) {
// the body is key1=val1&key2=val2&key3=val3 and the Webserver has already parsed it
String str;
if (server.hasArg("var1")) {
str = server.arg("var1");
var1 = str.toInt();
}
if (server.hasArg("var2")) {
str = server.arg("var2");
var2 = str.toInt();
}
if (server.hasArg("var3")) {
str = server.arg("var3");
var3 = str.toInt();
}
handleStaticFile("/reload_success.html");
} else if (server.hasArg("plain")) {
// parse the body as JSON object
String body = server.arg("plain");
StaticJsonBuffer<200> jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(body);
if (!root.success()) {
handleStaticFile("/reload_failure.html");
return;
}
if (root.containsKey("var1"))
var1 = root["var1"];
if (root.containsKey("var2"))
var2 = root["var2"];
if (root.containsKey("var3"))
var3 = root["var3"];
handleStaticFile("/reload_success.html");
} else {
handleStaticFile("/reload_failure.html");
return; // do not save the configuration
}
saveConfig();
}
void setup() {
Serial.begin(115200);
while (!Serial) {
;
}
Serial.println("");
Serial.println("setup");
// The SPIFFS file system contains the config.json, and the html and javascript code for the web interface
SPIFFS.begin();
loadConfig();
printConfig();
WiFiManager wifiManager;
wifiManager.setAPStaticIPConfig(IPAddress(192, 168, 1, 1), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));
wifiManager.autoConnect(host);
Serial.println("connected");
// this serves all URIs that can be resolved to a file on the SPIFFS filesystem
server.onNotFound(handleNotFound);
server.on("/", HTTP_GET, []() {
handleRedirect("/index.html");
});
server.on("/defaults", HTTP_GET, []() {
Serial.println("handleDefaults");
handleStaticFile("/reload_success.html");
defaultConfig();
printConfig();
saveConfig();
server.close();
server.stop();
delay(5000);
ESP.restart();
});
server.on("/reconnect", HTTP_GET, []() {
Serial.println("handleReconnect");
handleStaticFile("/reload_success.html");
server.close();
server.stop();
delay(5000);
WiFiManager wifiManager;
wifiManager.resetSettings();
wifiManager.setAPStaticIPConfig(IPAddress(192, 168, 1, 1), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));
wifiManager.startConfigPortal(host);
server.begin();
if (WiFi.status() == WL_CONNECTED)
Serial.println("WiFi status ok");
});
server.on("/restart", HTTP_GET, []() {
Serial.println("handleRestart");
handleStaticFile("/reload_success.html");
server.close();
server.stop();
SPIFFS.end();
delay(5000);
ESP.restart();
});
server.on("/json", HTTP_PUT, handleJSON);
server.on("/json", HTTP_POST, handleJSON);
server.on("/json", HTTP_GET, [] {
StaticJsonBuffer<200> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root["var1"] = var1;
root["var2"] = var2;
root["var3"] = var3;
root["version"] = version;
root["uptime"] = long(millis() / 1000);
String content;
root.printTo(content);
server.send(200, "application/json", content);
});
// ask server to track these headers
const char* headerkeys[] = { "User-Agent", "Content-Type" };
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
server.collectHeaders(headerkeys, headerkeyssize);
// start the web server
server.begin();
// announce the hostname and web server through zeroconf
MDNS.begin(host);
MDNS.addService("http", "tcp", 80);
Serial.println("Setup done");
}
void loop() {
// put your main code here, to run repeatedly
server.handleClient();
delay(10); // in milliseconds
}
================================================
FILE: esp32_config_webinterface_v6/README.md
================================================
# Overview
This is a demonstration and test sketch for configuring persistent options via the web interface. This strategy is used in a number of my functional ESP32 and ESP8266 sketches. It consists of a `settings.json` file in the SPIFFS filesystem and a `settings.html` file with some javascript.
**This specific version is for ArduinoJson 6, there are other examples for version 5 and 7.**
The settings can be changed in the webbrowser at http://192.168.1.xxx/settings and clicking "Send". This results in `Webserver` parsing the arguments and placing the variables in the header, but also places the query string in the body.
The following also results in `Webserver` parsing the arguments and placing the variables in the header.
curl -X POST 'http://192.168.1.xxx/json?var1=11&var2=22&var3=33'
curl -X PUT 'http://192.168.1.xxx/json?var1=11&var2=22&var3=33'
The following is not parsed, but only places the JSON data in the body.
curl -X POST http://192.168.1.xxx/json -d '{"var1":11,"var2":22,"var3":33}'
curl -X PUT http://192.168.1.xxx/json -d '{"var1":11,"var2":22,"var3":33}'
## SPIFFS for static files
You should not only write the firmware to the ESP32 module, but also the static content for the web interface. The html, css and javascript files located in the data directory should be written to the SPIFS filesystem on the ESP32. See https://github.com/me-no-dev/arduino-esp32fs-plugin.
You will get a "file not found" error if the firmware cannot access the data files.
## Arduino ESP32 filesystem uploader
This Arduino sketch includes a `data` directory with a number of files that should be uploaded to the ESP32 using the [SPIFFS filesystem uploader](https://github.com/me-no-dev/arduino-esp32fs-plugin) tool. At the moment (Feb 2024) the Arduino 2.x IDE does *not* support the SPIFFS filesystem uploader plugin. You have to use the Arduino 1.8.x IDE (recommended), or the command line utilities for uploading the data.
================================================
FILE: esp32_config_webinterface_v6/data/config.json
================================================
{
"var1":10,
"var2":20,
"var3":30
}
================================================
FILE: esp32_config_webinterface_v6/data/hello.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Hello</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<p>Hello panda!</p>
<img src="panda.jpg">
</body>
</html>
================================================
FILE: esp32_config_webinterface_v6/data/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Index</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<a href="/hello.html">Hello</a></br>
<a href="/monitor.html">Monitor</a></br>
<a href="/settings.html">Change settings</a></br>
<a href="/reconnect">Reconnect WiFi</a></br>
<a href="/defaults">Default settings</a></br>
<a href="/restart">Restart hardware</a></br>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v6/data/monitor.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Monitor</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>Monitor</h1>
Firmware version:
<div id="version" name="version">?</div>
Uptime:
<div id="uptime" name"uptime">?</div>
<script>
async function updateContent() {
const response = await fetch("json");
const data = await response.json();
console.log(data);
document.getElementById("version").innerHTML = data["version"];
document.getElementById("uptime").innerHTML = data["uptime"];
}
updateContent();
</script>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v6/data/reload_failure.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Failure</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<p>Failure</p>
<script type="text/javascript">
setTimeout(function(){location="/index.html"},1000);
</script>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v6/data/reload_success.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Success</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<p>Success</p>
<script type="text/javascript">
setTimeout(function(){location="/index.html"},1000);
</script>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v6/data/settings.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Settings</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>Settings</h1>
<form id="settings-form" method="post" action="json">
<div class="field">
<label for="var1">var1:</label>
<input type="text" id="var1" name="var1" value="?" required>
</div>
<div class="field">
<label for="var2">var2:</label>
<input type="text" id="var2" name="var2" value="?" required>
</div>
<div class="field">
<label for="var2">var3:</label>
<input type="text" id="var3" name="var3" value="?" required>
</div>
<div class="field">
<button type="submit">Save</button>
</div>
</form>
<script>
async function updateContent() {
const response = await fetch("json");
const data = await response.json();
console.log(data);
document.getElementById("var1").value = data["var1"];
document.getElementById("var2").value = data["var2"];
document.getElementById("var3").value = data["var3"];
}
updateContent();
</script>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v6/data/style.css
================================================
.c{
text-align: center;
}
div,input{
padding:5px;font-size:1em;
}
input{
width:95%;
}
body{
text-align: center;font-family:verdana;
}
button{
border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;
}
.q{
float: right;width: 64px;text-align: right;
}
.l{
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAALVBMVEX///8EBwfBwsLw8PAzNjaCg4NTVVUjJiZDRUUUFxdiZGSho6OSk5Pg4eFydHTCjaf3AAAAZElEQVQ4je2NSw7AIAhEBamKn97/uMXEGBvozkWb9C2Zx4xzWykBhFAeYp9gkLyZE0zIMno9n4g19hmdY39scwqVkOXaxph0ZCXQcqxSpgQpONa59wkRDOL93eAXvimwlbPbwwVAegLS1HGfZAAAAABJRU5ErkJggg==") no-repeat left center;background-size: 1em;
}
================================================
FILE: esp32_config_webinterface_v6/esp32_config_webinterface_v6.ino
================================================
#if not defined(ESP32)
#error This is a sketch for an ESP32 board, like the NodeMCU 32S, LOLIN32 Lite, or the Adafruit Huzzah32
#endif
#include <Arduino.h>
#include <ArduinoJson.h> // https://arduinojson.org
#include <WiFi.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <FS.h>
#include <SPIFFS.h>
#include "webinterface.h"
/*********************************************************************************/
const char* host = "ESP32";
const char* version = __DATE__ " / " __TIME__;
WebServer server(80);
WiFiManager wifiManager;
/*********************************************************************************/
void setup() {
Serial.begin(115200);
while (!Serial) {
;
}
Serial.println("Setup starting");
// The SPIFFS file system contains the config.json, and the html and javascript code for the web interface
SPIFFS.begin();
loadConfig();
printConfig();
WiFiManager wifiManager;
wifiManager.setAPStaticIPConfig(IPAddress(192, 168, 1, 1), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));
wifiManager.autoConnect(host);
Serial.println("connected");
// this serves all URIs that can be resolved to a file on the SPIFFS filesystem
server.onNotFound(handleNotFound);
server.on("/", HTTP_GET, []() {
handleRedirect("/index.html");
});
server.on("/defaults", HTTP_GET, []() {
Serial.println("handleDefaults");
handleStaticFile("/reload_success.html");
defaultConfig();
printConfig();
saveConfig();
server.close();
server.stop();
ESP.restart();
});
server.on("/reconnect", HTTP_GET, []() {
Serial.println("handleReconnect");
handleStaticFile("/reload_success.html");
server.close();
server.stop();
delay(5000);
WiFiManager wifiManager;
wifiManager.resetSettings();
wifiManager.setAPStaticIPConfig(IPAddress(192, 168, 1, 1), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));
wifiManager.startConfigPortal(host);
server.begin();
if (WiFi.status() == WL_CONNECTED)
Serial.println("WiFi status ok");
});
server.on("/restart", HTTP_GET, []() {
Serial.println("handleRestart");
handleStaticFile("/reload_success.html");
server.close();
server.stop();
SPIFFS.end();
delay(5000);
ESP.restart();
});
server.on("/json", HTTP_PUT, handleJSON);
server.on("/json", HTTP_POST, handleJSON);
server.on("/json", HTTP_GET, [] {
DynamicJsonDocument root(300);
N_CONFIG_TO_JSON(var1, "var1");
N_CONFIG_TO_JSON(var2, "var2");
N_CONFIG_TO_JSON(var3, "var3");
root["version"] = version;
root["uptime"] = long(millis() / 1000);
String str;
serializeJson(root, str);
server.setContentLength(str.length());
server.send(200, "application/json", str);
});
// start the web server
server.begin();
// announce the hostname and web server through zeroconf
MDNS.begin(host);
MDNS.addService("http", "tcp", 80);
Serial.println("Setup done");
}
/*********************************************************************************/
void loop() {
server.handleClient();
delay(10); // in milliseconds
}
================================================
FILE: esp32_config_webinterface_v6/webinterface.cpp
================================================
#include "webinterface.h"
Config config;
extern WebServer server;
/***************************************************************************/
static String getContentType(const String& path) {
if (path.endsWith(".html")) return "text/html";
else if (path.endsWith(".htm")) return "text/html";
else if (path.endsWith(".css")) return "text/css";
else if (path.endsWith(".txt")) return "text/plain";
else if (path.endsWith(".js")) return "application/javascript";
else if (path.endsWith(".png")) return "image/png";
else if (path.endsWith(".gif")) return "image/gif";
else if (path.endsWith(".jpg")) return "image/jpeg";
else if (path.endsWith(".jpeg")) return "image/jpeg";
else if (path.endsWith(".ico")) return "image/x-icon";
else if (path.endsWith(".svg")) return "image/svg+xml";
else if (path.endsWith(".xml")) return "text/xml";
else if (path.endsWith(".pdf")) return "application/pdf";
else if (path.endsWith(".zip")) return "application/zip";
else if (path.endsWith(".gz")) return "application/x-gzip";
else if (path.endsWith(".json")) return "application/json";
return "application/octet-stream";
}
/***************************************************************************/
bool defaultConfig() {
Serial.println("defaultConfig");
config.var1 = 11;
config.var2 = 22;
config.var3 = 33;
return true;
}
bool loadConfig() {
Serial.println("loadConfig");
File configFile = SPIFFS.open("/config.json", "r");
if (!configFile) {
Serial.println("Failed to open config file");
return false;
}
size_t size = configFile.size();
if (size > 1024) {
Serial.println("Config file size is too large");
return false;
}
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
configFile.close();
StaticJsonDocument<300> jsonBuffer;
DynamicJsonDocument root(1024);
DeserializationError error = deserializeJson(root, buf.get());
if (error) {
Serial.println("Failed to parse config file");
return false;
}
N_JSON_TO_CONFIG(var1, "var1");
N_JSON_TO_CONFIG(var2, "var2");
N_JSON_TO_CONFIG(var3, "var3");
return true;
}
bool saveConfig() {
Serial.println("saveConfig");
DynamicJsonDocument root(300);
N_CONFIG_TO_JSON(var1, "var1");
N_CONFIG_TO_JSON(var2, "var2");
N_CONFIG_TO_JSON(var3, "var3");
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
Serial.println("Failed to open config file for writing");
return false;
}
else {
Serial.println("Writing to config file");
serializeJson(root, configFile);
configFile.close();
return true;
}
}
void printConfig() {
Serial.print("var1 = ");
Serial.println(config.var1);
Serial.print("var2 = ");
Serial.println(config.var2);
Serial.print("var3 = ");
Serial.println(config.var3);
}
void printRequest() {
String message = "HTTP Request\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nHeaders: ";
message += server.headers();
message += "\n";
for (uint8_t i = 0; i < server.headers(); i++ ) {
message += " " + server.headerName(i) + ": " + server.header(i) + "\n";
}
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
Serial.println(message);
}
void handleNotFound() {
Serial.print("handleNotFound: ");
Serial.println(server.uri());
if (SPIFFS.exists(server.uri())) {
handleStaticFile(server.uri());
}
else {
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.setContentLength(message.length());
server.send(404, "text/plain", message);
}
}
void handleRedirect(const char * filename) {
handleRedirect((String)filename);
}
void handleRedirect(String filename) {
Serial.println("handleRedirect: " + filename);
server.sendHeader("Location", filename, true);
server.setContentLength(0);
server.send(302, "text/plain", "");
}
bool handleStaticFile(const char * path) {
return handleStaticFile((String)path);
}
bool handleStaticFile(String path) {
Serial.println("handleStaticFile: " + path);
String contentType = getContentType(path); // Get the MIME type
if (SPIFFS.exists(path)) { // If the file exists
File file = SPIFFS.open(path, "r"); // Open it
server.setContentLength(file.size());
server.streamFile(file, contentType); // And send it to the client
file.close(); // Then close the file again
return true;
}
Serial.println("\tFile Not Found");
return false; // If the file doesn't exist, return false
}
void handleJSON() {
// this gets called in response to either a PUT or a POST
Serial.println("handleJSON");
printRequest();
if (server.hasArg("var1") || server.hasArg("var2") || server.hasArg("var3")) {
// the body is key1=val1&key2=val2&key3=val3 and the ESP8266Webserver has already parsed it
N_KEYVAL_TO_CONFIG(var1, "var1");
N_KEYVAL_TO_CONFIG(var2, "var2");
N_KEYVAL_TO_CONFIG(var3, "var3");
handleStaticFile("/reload_success.html");
}
else if (server.hasArg("plain")) {
// parse the body as JSON object
DynamicJsonDocument root(300);
DeserializationError error = deserializeJson(root, server.arg("plain"));
if (error) {
handleStaticFile("/reload_failure.html");
return;
}
N_JSON_TO_CONFIG(var1, "var1");
N_JSON_TO_CONFIG(var2, "var2");
N_JSON_TO_CONFIG(var3, "var3");
handleStaticFile("/reload_success.html");
}
else {
handleStaticFile("/reload_failure.html");
return; // do not save the configuration
}
saveConfig();
}
================================================
FILE: esp32_config_webinterface_v6/webinterface.h
================================================
#ifndef _WEBINTERFACE_H_
#define _WEBINTERFACE_H_
#include <Arduino.h>
#include <WebServer.h>
#include <ArduinoJson.h> // https://arduinojson.org
#include <FS.h>
#include <SPIFFS.h>
#ifndef ARDUINOJSON_VERSION
#error ArduinoJson version 6 not found, please include ArduinoJson.h in your .ino file
#endif
#if ARDUINOJSON_VERSION_MAJOR != 6
#error ArduinoJson version 6 is required
#endif
/* these are for numbers */
#define N_JSON_TO_CONFIG(x, y) { if (root.containsKey(y)) { config.x = root[y]; } }
#define N_CONFIG_TO_JSON(x, y) { root[y] = config.x; }
#define N_KEYVAL_TO_CONFIG(x, y) { if (server.hasArg(y)) { String str = server.arg(y); config.x = str.toFloat(); } }
/* these are for strings */
#define S_JSON_TO_CONFIG(x, y) { if (root.containsKey(y)) { strcpy(config.x, root[y]); } }
#define S_CONFIG_TO_JSON(x, y) { root.set(y, config.x); }
#define S_KEYVAL_TO_CONFIG(x, y) { if (server.hasArg(y)) { String str = server.arg(y); strcpy(config.x, str.c_str()); } }
struct Config {
int var1;
int var2;
int var3;
};
extern Config config;
bool defaultConfig(void);
bool loadConfig(void);
bool saveConfig(void);
void printConfig(void);
void handleNotFound(void);
void handleRedirect(String);
void handleRedirect(const char *);
bool handleStaticFile(String);
bool handleStaticFile(const char *);
void handleJSON();
#endif // _WEBINTERFACE_H_
================================================
FILE: esp32_config_webinterface_v7/README.md
================================================
# Overview
This is a demonstration and test sketch for configuring persistent options via the web interface. This strategy is used in a number of my functional ESP32 and ESP8266 sketches. It consists of a `settings.json` file in the SPIFFS filesystem and a `settings.html` file with some javascript.
**This specific version is for ArduinoJson 7, there are other examples for version 5 and 6.**
The settings can be changed in the webbrowser at http://192.168.1.xxx/settings and clicking "Send". This results in `Webserver` parsing the arguments and placing the variables in the header, but also places the query string in the body.
The following also results in `Webserver` parsing the arguments and placing the variables in the header.
curl -X POST 'http://192.168.1.xxx/json?var1=11&var2=22&var3=33'
curl -X PUT 'http://192.168.1.xxx/json?var1=11&var2=22&var3=33'
The following is not parsed, but only places the JSON data in the body.
curl -X POST http://192.168.1.xxx/json -d '{"var1":11,"var2":22,"var3":33}'
curl -X PUT http://192.168.1.xxx/json -d '{"var1":11,"var2":22,"var3":33}'
## SPIFFS for static files
You should not only write the firmware to the ESP32 module, but also the static content for the web interface. The html, css and javascript files located in the data directory should be written to the SPIFS filesystem on the ESP32. See https://github.com/me-no-dev/arduino-esp32fs-plugin.
You will get a "file not found" error if the firmware cannot access the data files.
## Arduino ESP32 filesystem uploader
This Arduino sketch includes a `data` directory with a number of files that should be uploaded to the ESP32 using the [SPIFFS filesystem uploader](https://github.com/me-no-dev/arduino-esp32fs-plugin) tool. At the moment (Feb 2024) the Arduino 2.x IDE does *not* support the SPIFFS filesystem uploader plugin. You have to use the Arduino 1.8.x IDE (recommended), or the command line utilities for uploading the data.
================================================
FILE: esp32_config_webinterface_v7/data/config.json
================================================
{
"var1":10,
"var2":20,
"var3":30
}
================================================
FILE: esp32_config_webinterface_v7/data/hello.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Hello</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<p>Hello panda!</p>
<img src="panda.jpg">
</body>
</html>
================================================
FILE: esp32_config_webinterface_v7/data/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Index</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<a href="/hello.html">Hello</a></br>
<a href="/monitor.html">Monitor</a></br>
<a href="/settings.html">Change settings</a></br>
<a href="/reconnect">Reconnect WiFi</a></br>
<a href="/defaults">Default settings</a></br>
<a href="/restart">Restart hardware</a></br>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v7/data/monitor.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Monitor</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>Monitor</h1>
Firmware version:
<div id="version" name="version">?</div>
Uptime:
<div id="uptime" name"uptime">?</div>
<script>
async function updateContent() {
const response = await fetch("json");
const data = await response.json();
console.log(data);
document.getElementById("version").innerHTML = data["version"];
document.getElementById("uptime").innerHTML = data["uptime"];
}
updateContent();
</script>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v7/data/reload_failure.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Failure</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<p>Failure</p>
<script type="text/javascript">
setTimeout(function(){location="/index.html"},1000);
</script>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v7/data/reload_success.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Success</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<p>Success</p>
<script type="text/javascript">
setTimeout(function(){location="/index.html"},1000);
</script>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v7/data/settings.html
================================================
<!doctype html>
<html lang="en">
<head>
<title>Settings</title>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>Settings</h1>
<form id="settings-form" method="post" action="json">
<div class="field">
<label for="var1">var1:</label>
<input type="text" id="var1" name="var1" value="?" required>
</div>
<div class="field">
<label for="var2">var2:</label>
<input type="text" id="var2" name="var2" value="?" required>
</div>
<div class="field">
<label for="var2">var3:</label>
<input type="text" id="var3" name="var3" value="?" required>
</div>
<div class="field">
<button type="submit">Save</button>
</div>
</form>
<script>
async function updateContent() {
const response = await fetch("json");
const data = await response.json();
console.log(data);
document.getElementById("var1").value = data["var1"];
document.getElementById("var2").value = data["var2"];
document.getElementById("var3").value = data["var3"];
}
updateContent();
</script>
</body>
</html>
================================================
FILE: esp32_config_webinterface_v7/data/style.css
================================================
.c{
text-align: center;
}
div,input{
padding:5px;font-size:1em;
}
input{
width:95%;
}
body{
text-align: center;font-family:verdana;
}
button{
border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;
}
.q{
float: right;width: 64px;text-align: right;
}
.l{
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAALVBMVEX///8EBwfBwsLw8PAzNjaCg4NTVVUjJiZDRUUUFxdiZGSho6OSk5Pg4eFydHTCjaf3AAAAZElEQVQ4je2NSw7AIAhEBamKn97/uMXEGBvozkWb9C2Zx4xzWykBhFAeYp9gkLyZE0zIMno9n4g19hmdY39scwqVkOXaxph0ZCXQcqxSpgQpONa59wkRDOL93eAXvimwlbPbwwVAegLS1HGfZAAAAABJRU5ErkJggg==") no-repeat left center;background-size: 1em;
}
================================================
FILE: esp32_config_webinterface_v7/esp32_config_webinterface_v7.ino
================================================
#if not defined(ESP32)
#error This is a sketch for an ESP32 board, like the NodeMCU 32S, LOLIN32 Lite, or the Adafruit Huzzah32
#endif
#include <Arduino.h>
#include <ArduinoJson.h> // https://arduinojson.org
#include <WiFi.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <FS.h>
#include <SPIFFS.h>
#include "webinterface.h"
/*********************************************************************************/
const char* host = "ESP32";
const char* version = __DATE__ " / " __TIME__;
WebServer server(80);
WiFiManager wifiManager;
/*********************************************************************************/
void setup() {
Serial.begin(115200);
while (!Serial) {
;
}
Serial.println("Setup starting");
// The SPIFFS file system contains the config.json, and the html and javascript code for the web interface
SPIFFS.begin();
loadConfig();
printConfig();
WiFiManager wifiManager;
wifiManager.setAPStaticIPConfig(IPAddress(192, 168, 1, 1), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));
wifiManager.autoConnect(host);
Serial.println("connected");
// this serves all URIs that can be resolved to a file on the SPIFFS filesystem
server.onNotFound(handleNotFound);
server.on("/", HTTP_GET, []() {
handleRedirect("/index.html");
});
server.on("/defaults", HTTP_GET, []() {
Serial.println("handleDefaults");
handleStaticFile("/reload_success.html");
defaultConfig();
printConfig();
saveConfig();
server.close();
server.stop();
ESP.restart();
});
server.on("/reconnect", HTTP_GET, []() {
Serial.println("handleReconnect");
handleStaticFile("/reload_success.html");
server.close();
server.stop();
delay(5000);
WiFiManager wifiManager;
wifiManager.resetSettings();
wifiManager.setAPStaticIPConfig(IPAddress(192, 168, 1, 1), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));
wifiManager.startConfigPortal(host);
server.begin();
if (WiFi.status() == WL_CONNECTED)
Serial.println("WiFi status ok");
});
server.on("/restart", HTTP_GET, []() {
Serial.println("handleRestart");
handleStaticFile("/reload_success.html");
server.close();
server.stop();
SPIFFS.end();
delay(5000);
ESP.restart();
});
server.on("/json", HTTP_PUT, handleJSON);
server.on("/json", HTTP_POST, handleJSON);
server.on("/json", HTTP_GET, [] {
JsonDocument root;
N_CONFIG_TO_JSON(var1, "var1");
N_CONFIG_TO_JSON(var2, "var2");
N_CONFIG_TO_JSON(var3, "var3");
root["version"] = version;
root["uptime"] = long(millis() / 1000);
String str;
serializeJson(root, str);
server.setContentLength(str.length());
server.send(200, "application/json", str);
});
// start the web server
server.begin();
// announce the hostname and web server through zeroconf
MDNS.begin(host);
MDNS.addService("http", "tcp", 80);
Serial.println("Setup done");
}
/*********************************************************************************/
void loop() {
server.handleClient();
delay(10); // in milliseconds
}
================================================
FILE: esp32_config_webinterface_v7/webinterface.cpp
================================================
#include "webinterface.h"
Config config;
extern WebServer server;
/***************************************************************************/
static String getContentType(const String& path) {
if (path.endsWith(".html")) return "text/html";
else if (path.endsWith(".htm")) return "text/html";
else if (path.endsWith(".css")) return "text/css";
else if (path.endsWith(".txt")) return "text/plain";
else if (path.endsWith(".js")) return "application/javascript";
else if (path.endsWith(".png")) return "image/png";
else if (path.endsWith(".gif")) return "image/gif";
else if (path.endsWith(".jpg")) return "image/jpeg";
else if (path.endsWith(".jpeg")) return "image/jpeg";
else if (path.endsWith(".ico")) return "image/x-icon";
else if (path.endsWith(".svg")) return "image/svg+xml";
else if (path.endsWith(".xml")) return "text/xml";
else if (path.endsWith(".pdf")) return "application/pdf";
else if (path.endsWith(".zip")) return "application/zip";
else if (path.endsWith(".gz")) return "application/x-gzip";
else if (path.endsWith(".json")) return "application/json";
return "application/octet-stream";
}
/***************************************************************************/
bool defaultConfig() {
Serial.println("defaultConfig");
config.var1 = 11;
config.var2 = 22;
config.var3 = 33;
return true;
}
bool loadConfig() {
Serial.println("loadConfig");
File configFile = SPIFFS.open("/config.json", "r");
if (!configFile) {
Serial.println("Failed to open config file");
return false;
}
size_t size = configFile.size();
if (size > 1024) {
Serial.println("Config file size is too large");
return false;
}
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
configFile.close();
JsonDocument root;
DeserializationError error = deserializeJson(root, buf.get());
if (error) {
Serial.println("Failed to parse config file");
return false;
}
N_JSON_TO_CONFIG(var1, "var1");
N_JSON_TO_CONFIG(var2, "var2");
N_JSON_TO_CONFIG(var3, "var3");
return true;
}
bool saveConfig() {
Serial.println("saveConfig");
JsonDocument root;
N_CONFIG_TO_JSON(var1, "var1");
N_CONFIG_TO_JSON(var2, "var2");
N_CONFIG_TO_JSON(var3, "var3");
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
Serial.println("Failed to open config file for writing");
return false;
}
else {
Serial.println("Writing to config file");
serializeJson(root, configFile);
configFile.close();
return true;
}
}
void printConfig() {
Serial.print("var1 = ");
Serial.println(config.var1);
Serial.print("var2 = ");
Serial.println(config.var2);
Serial.print("var3 = ");
Serial.println(config.var3);
}
void printRequest() {
String message = "HTTP Request\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nHeaders: ";
message += server.headers();
message += "\n";
for (uint8_t i = 0; i < server.headers(); i++ ) {
message += " " + server.headerName(i) + ": " + server.header(i) + "\n";
}
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
Serial.println(message);
}
void handleNotFound() {
Serial.print("handleNotFound: ");
Serial.println(server.uri());
if (SPIFFS.exists(server.uri())) {
handleStaticFile(server.uri());
}
else {
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.setContentLength(message.length());
server.send(404, "text/plain", message);
}
}
void handleRedirect(const char * filename) {
handleRedirect((String)filename);
}
void handleRedirect(String filename) {
Serial.println("handleRedirect: " + filename);
server.sendHeader("Location", filename, true);
server.setContentLength(0);
server.send(302, "text/plain", "");
}
bool handleStaticFile(const char * path) {
return handleStaticFile((String)path);
}
bool handleStaticFile(String path) {
Serial.println("handleStaticFile: " + path);
String contentType = getContentType(path); // Get the MIME type
if (SPIFFS.exists(path)) { // If the file exists
File file = SPIFFS.open(path, "r"); // Open it
server.setContentLength(file.size());
server.streamFile(file, contentType); // And send it to the client
file.close(); // Then close the file again
return true;
}
Serial.println("\tFile Not Found");
return false; // If the file doesn't exist, return false
}
void handleJSON() {
// this gets called in response to either a PUT or a POST
Serial.println("handleJSON");
printRequest();
if (server.hasArg("var1") || server.hasArg("var2") || server.hasArg("var3")) {
// the body is key1=val1&key2=val2&key3=val3 and the ESP8266Webserver has already parsed it
N_KEYVAL_TO_CONFIG(var1, "var1");
N_KEYVAL_TO_CONFIG(var2, "var2");
N_KEYVAL_TO_CONFIG(var3, "var3");
handleStaticFile("/reload_success.html");
}
else if (server.hasArg("plain")) {
// parse the body as JSON object
JsonDocument root;
DeserializationError error = deserializeJson(root, server.arg("plain"));
if (error) {
handleStaticFile("/reload_failure.html");
return;
}
N_JSON_TO_CONFIG(var1, "var1");
N_JSON_TO_CONFIG(var2, "var2");
N_JSON_TO_CONFIG(var3, "var3");
handleStaticFile("/reload_success.html");
}
else {
handleStaticFile("/reload_failure.html");
return; // do not save the configuration
}
saveConfig();
}
================================================
FILE: esp32_config_webinterface_v7/webinterface.h
================================================
#ifndef _WEBINTERFACE_H_
#define _WEBINTERFACE_H_
#include <Arduino.h>
#include <WebServer.h>
#include <ArduinoJson.h> // https://arduinojson.org
#include <FS.h>
#include <SPIFFS.h>
#ifndef ARDUINOJSON_VERSION
#error ArduinoJson version 7 not found, please include ArduinoJson.h in your .ino file
#endif
#if ARDUINOJSON_VERSION_MAJOR != 7
#error ArduinoJson version 7 is required
#endif
/* these are for numbers */
#define N_JSON_TO_CONFIG(x, y) { if (root.containsKey(y)) { config.x = root[y]; } }
#define N_CONFIG_TO_JSON(x, y) { root[y] = config.x; }
#define N_KEYVAL_TO_CONFIG(x, y) { if (server.hasArg(y)) { String str = server.arg(y); config.x = str.toFloat(); } }
/* these are for strings */
#define S_JSON_TO_CONFIG(x, y) { if (root.containsKey(y)) { strcpy(config.x, root[y]); } }
#define S_CONFIG_TO_JSON(x, y) { root.set(y, config.x); }
#define S_KEYVAL_TO_CONFIG(x, y) { if (server.hasArg(y)) { String str = server.arg(y); strcpy(config.x, str.c_str()); } }
struct Config {
int var1;
int var2;
int var3;
};
extern Config config;
bool defaultConfig(void);
bool loadConfig(void);
bool saveConfig(void);
void printConfig(void);
void handleNotFound(vo
gitextract_yp2dqrl9/
├── .gitignore
├── .gitmodules
├── PulseSensor_v0/
│ ├── AllSerialHandling.cpp
│ ├── Interrupt.cpp
│ ├── PulseSensor_v0.ino
│ ├── README.md
│ └── Timer_Interrupt_Notes.cpp
├── PulseSensor_v1/
│ ├── PulseSensor_v1.ino
│ └── README.md
├── PulseSensor_v2/
│ ├── Interrupt.cpp
│ ├── PulseSensor_v2.ino
│ └── README.md
├── README.md
├── bitsi/
│ └── bitsi.ino
├── blenderdefender/
│ └── blenderdefender.ino
├── digispark_skateboard/
│ ├── README.md
│ ├── colorspace.cpp
│ ├── colorspace.h
│ ├── digispark_skateboard.ino
│ ├── neopixel_mode.cpp
│ └── neopixel_mode.h
├── eegsynth_cvgate_mcp4725/
│ ├── README.md
│ └── eegsynth_cvgate_mcp4725.ino
├── eegsynth_cvgate_mcp4822/
│ └── eegsynth_cvgate_mcp4822.ino
├── eegsynth_devirtualizer/
│ └── eegsynth_devirtualizer.ino
├── eegsynth_usbdmxpro/
│ ├── COPYING.txt
│ └── eegsynth_usbdmxpro.ino
├── esp32_3wd_servo/
│ ├── README.md
│ └── esp32_3wd_servo.ino
├── esp32_3wd_stepper/
│ ├── README.md
│ ├── blink_led.cpp
│ ├── blink_led.h
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ ├── style.css
│ │ ├── waypoints.html
│ │ ├── waypoints1.csv
│ │ ├── waypoints2.csv
│ │ ├── waypoints3.csv
│ │ ├── waypoints4.csv
│ │ ├── waypoints5.csv
│ │ ├── waypoints6.csv
│ │ ├── waypoints7.csv
│ │ └── waypoints8.csv
│ ├── esp32_3wd_stepper.ino
│ ├── parseosc.cpp
│ ├── parseosc.h
│ ├── stepper.cpp
│ ├── stepper.h
│ ├── util.cpp
│ ├── util.h
│ ├── waypoints.cpp
│ ├── waypoints.h
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp32_config_webinterface_v5/
│ ├── README.md
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ └── style.css
│ └── esp32_config_webinterface_v5.ino
├── esp32_config_webinterface_v6/
│ ├── README.md
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ └── style.css
│ ├── esp32_config_webinterface_v6.ino
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp32_config_webinterface_v7/
│ ├── README.md
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ └── style.css
│ ├── esp32_config_webinterface_v7.ino
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp32_exgpill/
│ ├── README.md
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ └── style.css
│ ├── esp32_exgpill.ino
│ ├── fieldtrip_buffer.cpp
│ ├── fieldtrip_buffer.h
│ ├── rgb_led.cpp
│ ├── rgb_led.h
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp32_inmp441/
│ ├── RunningStat.h
│ ├── esp32_inmp441.ino
│ └── util.h
├── esp32_sph0645/
│ ├── RunningStat.h
│ ├── compress.cpp
│ └── esp32_sph0645.ino
├── esp32c6_feeder/
│ ├── README.md
│ └── esp32c6_feeder.ino
├── esp8266_12v_trigger/
│ └── esp8266_12v_trigger.ino
├── esp8266_ad8232_ecg/
│ ├── README.md
│ ├── blink_led.cpp
│ ├── blink_led.h
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ ├── style.css
│ │ └── update.html
│ ├── esp8266_ad8232_ecg.ino
│ ├── fieldtrip_buffer.cpp
│ ├── fieldtrip_buffer.h
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp8266_artnet_bci/
│ └── esp8266_artnet_bci.ino
├── esp8266_artnet_neopixel/
│ ├── README.md
│ ├── colorspace.cpp
│ ├── colorspace.h
│ ├── data/
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ ├── style.css
│ │ └── update.html
│ ├── esp8266_artnet_neopixel.ino
│ ├── font8x8_basic.h
│ ├── neopixel_mode.cpp
│ ├── neopixel_mode.h
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp8266_config_spiffs/
│ ├── data/
│ │ └── config.json
│ └── esp8266_config_spiffs.ino
├── esp8266_config_webinterface/
│ ├── README.md
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ ├── style.css
│ │ └── update.html
│ └── esp8266_config_webinterface.ino
├── esp8266_fan_control/
│ └── esp8266_fan_control.ino
├── esp8266_fieldtrip_buffer/
│ ├── esp8266_fieldtrip_buffer.ino
│ ├── fieldtrip_buffer.cpp
│ └── fieldtrip_buffer.h
├── esp8266_imu_osc/
│ ├── I2Cscan.cpp
│ ├── I2Cscan.h
│ ├── README.md
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ ├── style.css
│ │ └── update.html
│ ├── esp8266_imu_osc.ino
│ ├── rgb_led.cpp
│ ├── rgb_led.h
│ ├── tca9548a.cpp
│ ├── tca9548a.h
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp8266_inmp411/
│ ├── RunningStat.h
│ └── esp8266_inmp411.ino
├── esp8266_p1_thingspeak/
│ └── esp8266_p1_thingspeak.ino
├── esp8266_polar_wearlink/
│ ├── README.md
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ ├── style.css
│ │ └── update.html
│ ├── esp8266_polar_wearlink.ino
│ ├── rgb_led.cpp
│ ├── rgb_led.h
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp8266_pulse_sensor/
│ ├── README.md
│ ├── blink_led.cpp
│ ├── blink_led.h
│ ├── data/
│ │ ├── config.json
│ │ ├── hello.html
│ │ ├── index.html
│ │ ├── monitor.html
│ │ ├── reload_failure.html
│ │ ├── reload_success.html
│ │ ├── settings.html
│ │ ├── style.css
│ │ └── update.html
│ ├── esp8266_pulse_sensor.ino
│ ├── fieldtrip_buffer.cpp
│ ├── fieldtrip_buffer.h
│ ├── webinterface.cpp
│ └── webinterface.h
├── esp8266_thingspeak/
│ └── esp8266_thingspeak.ino
├── m5dial_midi/
│ ├── colormap.h
│ └── m5dial_midi.ino
├── m5nanoc6_angle8_midi/
│ ├── colormap.h
│ └── m5nanoc6_angle8_midi.ino
├── m5nanoc6_encoder8_midi/
│ ├── colormap.h
│ └── m5nanoc6_encoder8_midi.ino
├── rfm12b_recv_xxxx/
│ ├── README.md
│ └── rfm12b_recv_xxxx.ino
├── rfm12b_send_am2301/
│ ├── README.md
│ └── rfm12b_send_am2301.ino
├── rfm12b_send_bmp085/
│ ├── README.md
│ └── rfm12b_send_bmp085.ino
├── rfm12b_send_cny70/
│ ├── README.md
│ └── rfm12b_send_cny70.ino
├── rfm12b_send_ds18b20/
│ ├── README.md
│ └── rfm12b_send_ds18b20.ino
├── rfm12b_send_lm35/
│ ├── README.md
│ └── rfm12b_send_lm35.ino
├── rfm12b_send_random/
│ ├── README.md
│ └── rfm12b_send_random.ino
├── rfm12b_thingspeak/
│ ├── README.md
│ └── rfm12b_thingspeak.ino
├── rp2040_dac7578/
│ ├── blink_led.cpp
│ ├── blink_led.h
│ └── rp2040_dac7578.ino
├── teensy_cvgate_mcp4725_neopixel/
│ ├── colormap.h
│ └── teensy_cvgate_mcp4725_neopixel.ino
├── teensy_gps_temp_ttn/
│ ├── payload.h
│ ├── sensor.cpp
│ └── teensy_gps_temp_ttn.ino
├── teensy_midifilter/
│ ├── midiname.c
│ └── teensy_midifilter.ino
├── teensy_ttn1/
│ └── teensy_ttn1.ino
├── teensy_ttn2/
│ └── teensy_ttn2.ino
└── uno_dac7578/
├── blink_led.cpp
├── blink_led.h
└── uno_dac7578.ino
SYMBOL INDEX (334 symbols across 57 files)
FILE: PulseSensor_v0/AllSerialHandling.cpp
function serialOutput (line 8) | void serialOutput(){ // Decide How To Output Serial.
function serialOutputWhenBeatHappens (line 18) | void serialOutputWhenBeatHappens(){
function sendDataToSerial (line 33) | void sendDataToSerial(char symbol, int data ){
function arduinoSerialMonitorVisual (line 41) | void arduinoSerialMonitorVisual(char symbol, int data ){
FILE: PulseSensor_v0/Interrupt.cpp
function interruptSetup (line 15) | void interruptSetup(){
function ISR (line 27) | ISR(TIMER2_COMPA_vect){ // triggered when Timer2...
FILE: PulseSensor_v2/Interrupt.cpp
function interruptSetup (line 12) | void interruptSetup() {
function ISR (line 24) | ISR(TIMER2_COMPA_vect) { // triggered when Timer2 c...
FILE: digispark_skateboard/colorspace.cpp
function hsv (line 3) | hsv rgb2hsv(rgb in)
function rgb (line 47) | rgb hsv2rgb(hsv in)
FILE: digispark_skateboard/colorspace.h
type rgb (line 4) | typedef struct {
type hsv (line 10) | typedef struct {
FILE: digispark_skateboard/neopixel_mode.cpp
function mode1 (line 21) | void mode1(uint8_t * data) {
function mode4 (line 37) | void mode4(uint8_t * data) {
function mode10 (line 65) | void mode10(uint8_t * data) {
function mode12 (line 95) | void mode12(uint8_t * data) {
function map_hsv_to_rgb (line 122) | void map_hsv_to_rgb(int *r, int *g, int *b) {
FILE: esp32_3wd_stepper/blink_led.cpp
function changeState (line 13) | void changeState() {
function ledInit (line 17) | void ledInit() {
function ledOn (line 21) | void ledOn() {
function ledOff (line 29) | void ledOff() {
function ledSlow (line 37) | void ledSlow() {
function ledMedium (line 45) | void ledMedium() {
function ledFast (line 53) | void ledFast() {
FILE: esp32_3wd_stepper/parseosc.cpp
function parseOSC (line 5) | void parseOSC() {
function printCallback (line 84) | void printCallback(OSCMessage &msg) {
function route1Callback (line 114) | void route1Callback(OSCMessage &msg) {
function route2Callback (line 119) | void route2Callback(OSCMessage &msg) {
function route3Callback (line 124) | void route3Callback(OSCMessage &msg) {
function route4Callback (line 129) | void route4Callback(OSCMessage &msg) {
function route5Callback (line 134) | void route5Callback(OSCMessage &msg) {
function route6Callback (line 139) | void route6Callback(OSCMessage &msg) {
function route7Callback (line 144) | void route7Callback(OSCMessage &msg) {
function route8Callback (line 149) | void route8Callback(OSCMessage &msg) {
function pauseCallback (line 154) | void pauseCallback(OSCMessage &msg) {
function resumeCallback (line 159) | void resumeCallback(OSCMessage &msg) {
function stopCallback (line 164) | void stopCallback(OSCMessage &msg) {
function resetCallback (line 169) | void resetCallback(OSCMessage & msg) {
function xCallback (line 174) | void xCallback(OSCMessage & msg) {
function yCallback (line 178) | void yCallback(OSCMessage & msg) {
function aCallback (line 182) | void aCallback(OSCMessage & msg) {
function dxCallback (line 186) | void dxCallback(OSCMessage & msg) {
function dyCallback (line 192) | void dyCallback(OSCMessage & msg) {
function daCallback (line 198) | void daCallback(OSCMessage & msg) {
function xyCallback (line 204) | void xyCallback(OSCMessage & msg) {
function accxyzCallback (line 209) | void accxyzCallback(OSCMessage & msg) {
FILE: esp32_3wd_stepper/stepper.h
function class (line 6) | class Stepper {
FILE: esp32_3wd_stepper/util.cpp
function printMacAddress (line 5) | void printMacAddress() {
function String (line 20) | String getMacAddress() {
function maxOfThree (line 41) | float maxOfThree(float a, float b, float c) {
FILE: esp32_3wd_stepper/waypoints.cpp
function printWaypoints (line 18) | void printWaypoints(int route) {
function saveWaypoints (line 37) | size_t saveWaypoints(int route, String s) {
function String (line 60) | String loadWaypoints(int route) {
function parseWaypoints (line 89) | void parseWaypoints(int route) {
FILE: esp32_3wd_stepper/webinterface.cpp
function String (line 9) | static String getContentType(const String& path) {
function defaultConfig (line 31) | bool defaultConfig() {
function loadConfig (line 41) | bool loadConfig() {
function saveConfig (line 75) | bool saveConfig() {
function printConfig (line 96) | void printConfig() {
function printRequest (line 107) | void printRequest() {
function handleNotFound (line 128) | void handleNotFound() {
function handleRedirect (line 150) | void handleRedirect(const char* filename) {
function handleRedirect (line 154) | void handleRedirect(String filename) {
function handleStaticFile (line 161) | bool handleStaticFile(const char* path) {
function handleStaticFile (line 165) | bool handleStaticFile(String path) {
function handleJSON (line 179) | void handleJSON() {
FILE: esp32_3wd_stepper/webinterface.h
type Config (line 28) | struct Config {
FILE: esp32_config_webinterface_v6/webinterface.cpp
function String (line 8) | static String getContentType(const String& path) {
function defaultConfig (line 30) | bool defaultConfig() {
function loadConfig (line 39) | bool loadConfig() {
function saveConfig (line 73) | bool saveConfig() {
function printConfig (line 94) | void printConfig() {
function printRequest (line 103) | void printRequest() {
function handleNotFound (line 124) | void handleNotFound() {
function handleRedirect (line 147) | void handleRedirect(const char * filename) {
function handleRedirect (line 151) | void handleRedirect(String filename) {
function handleStaticFile (line 158) | bool handleStaticFile(const char * path) {
function handleStaticFile (line 162) | bool handleStaticFile(String path) {
function handleJSON (line 176) | void handleJSON() {
FILE: esp32_config_webinterface_v6/webinterface.h
type Config (line 28) | struct Config {
FILE: esp32_config_webinterface_v7/webinterface.cpp
function String (line 8) | static String getContentType(const String& path) {
function defaultConfig (line 30) | bool defaultConfig() {
function loadConfig (line 39) | bool loadConfig() {
function saveConfig (line 72) | bool saveConfig() {
function printConfig (line 93) | void printConfig() {
function printRequest (line 102) | void printRequest() {
function handleNotFound (line 123) | void handleNotFound() {
function handleRedirect (line 146) | void handleRedirect(const char * filename) {
function handleRedirect (line 150) | void handleRedirect(String filename) {
function handleStaticFile (line 157) | bool handleStaticFile(const char * path) {
function handleStaticFile (line 161) | bool handleStaticFile(String path) {
function handleJSON (line 175) | void handleJSON() {
FILE: esp32_config_webinterface_v7/webinterface.h
type Config (line 28) | struct Config {
FILE: esp32_exgpill/fieldtrip_buffer.cpp
function fieldtrip_open_connection (line 11) | int fieldtrip_open_connection(const char *address, int port) {
function fieldtrip_close_connection (line 24) | int fieldtrip_close_connection(int s) {
function fieldtrip_write_header (line 33) | int fieldtrip_write_header(int server, uint32_t datatype, uint32_t nchan...
function fieldtrip_write_data (line 85) | int fieldtrip_write_data(int server, uint32_t datatype, uint32_t nchans,...
function wordsize_from_type (line 133) | int wordsize_from_type(uint32_t datatype) {
FILE: esp32_exgpill/fieldtrip_buffer.h
type messagedef_t (line 61) | typedef struct {
type headerdef_t (line 69) | typedef struct {
type datadef_t (line 80) | typedef struct {
type eventdef_t (line 89) | typedef struct {
FILE: esp32_exgpill/rgb_led.cpp
type LedColor (line 13) | enum LedColor {
type LedState (line 20) | enum LedState {
function ledInit (line 28) | void ledInit() {
function ledSet (line 121) | void ledSet(LedColor color, LedState state){
function toggleLed (line 151) | void toggleLed() {
FILE: esp32_exgpill/webinterface.cpp
function String (line 13) | static String getContentType(const String& path) {
function defaultConfig (line 33) | bool defaultConfig() {
function loadConfig (line 40) | bool loadConfig() {
function saveConfig (line 74) | bool saveConfig() {
function printConfig (line 93) | void printConfig() {
function printRequest (line 100) | void printRequest() {
function handleNotFound (line 121) | void handleNotFound() {
function handleRedirect (line 144) | void handleRedirect(const char * filename) {
function handleRedirect (line 148) | void handleRedirect(String filename) {
function handleStaticFile (line 155) | bool handleStaticFile(const char * path) {
function handleStaticFile (line 159) | bool handleStaticFile(String path) {
function handleJSON (line 175) | void handleJSON() {
FILE: esp32_exgpill/webinterface.h
type Config (line 4) | struct Config {
FILE: esp32_inmp441/RunningStat.h
function class (line 8) | class RunningStat
FILE: esp32_inmp441/util.h
function print_binary (line 4) | void print_binary(uint32_t val) {
FILE: esp32_sph0645/RunningStat.h
function class (line 5) | class RunningStat
FILE: esp32_sph0645/compress.cpp
function compress (line 3) | uint32_t compress(uint32_t *data, uint32_t nsamples, uint8_t *dest) {
function decompress (line 14) | uint32_t decompress(uint32_t *dest, uint32_t nsamples, uint8_t *data) {
FILE: esp8266_ad8232_ecg/blink_led.cpp
function changeState (line 13) | void changeState() {
function ledInit (line 17) | void ledInit() {
function ledOn (line 21) | void ledOn() {
function ledOff (line 29) | void ledOff() {
function ledSlow (line 37) | void ledSlow() {
function ledMedium (line 45) | void ledMedium() {
function ledFast (line 53) | void ledFast() {
FILE: esp8266_ad8232_ecg/fieldtrip_buffer.cpp
function fieldtrip_open_connection (line 11) | int fieldtrip_open_connection(const char *address, int port) {
function fieldtrip_close_connection (line 25) | int fieldtrip_close_connection(int s) {
function fieldtrip_write_header (line 37) | int fieldtrip_write_header(int server, uint32_t datatype, uint32_t nchan...
function fieldtrip_write_data (line 97) | int fieldtrip_write_data(int server, uint32_t datatype, uint32_t nchans,...
function wordsize_from_type (line 153) | int wordsize_from_type(uint32_t datatype) {
FILE: esp8266_ad8232_ecg/fieldtrip_buffer.h
type messagedef_t (line 61) | typedef struct {
type headerdef_t (line 69) | typedef struct {
type datadef_t (line 80) | typedef struct {
type eventdef_t (line 89) | typedef struct {
FILE: esp8266_ad8232_ecg/webinterface.cpp
function String (line 9) | static String getContentType(const String& path) {
function defaultConfig (line 31) | bool defaultConfig() {
function loadConfig (line 39) | bool loadConfig() {
function saveConfig (line 72) | bool saveConfig() {
function printRequest (line 94) | void printRequest() {
function handleUpdate1 (line 115) | void handleUpdate1() {
function handleUpdate2 (line 122) | void handleUpdate2() {
function handleDirList (line 147) | void handleDirList() {
function handleNotFound (line 160) | void handleNotFound() {
function handleRedirect (line 183) | void handleRedirect(const char * filename) {
function handleRedirect (line 187) | void handleRedirect(String filename) {
function handleStaticFile (line 194) | bool handleStaticFile(const char * path) {
function handleStaticFile (line 198) | bool handleStaticFile(String path) {
function handleJSON (line 212) | void handleJSON() {
FILE: esp8266_ad8232_ecg/webinterface.h
type Config (line 29) | struct Config {
FILE: esp8266_artnet_neopixel/colorspace.cpp
function hsv (line 3) | hsv rgb2hsv(rgb in)
function rgb (line 47) | rgb hsv2rgb(hsv in)
FILE: esp8266_artnet_neopixel/colorspace.h
type rgb (line 4) | typedef struct {
type hsv (line 10) | typedef struct {
FILE: esp8266_artnet_neopixel/neopixel_mode.cpp
function mode0 (line 42) | void mode0(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t...
function mode1 (line 79) | void mode1(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t...
function mode2 (line 128) | void mode2(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t...
function mode3 (line 189) | void mode3(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t...
function mode4 (line 281) | void mode4(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t...
function mode5 (line 368) | void mode5(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t...
function mode6 (line 443) | void mode6(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t...
function mode7 (line 516) | void mode7(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t...
function mode8 (line 591) | void mode8(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t...
function mode9 (line 663) | void mode9(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t...
function mode10 (line 747) | void mode10(uint16_t universe, uint16_t length, uint8_t sequence, uint8_...
function mode11 (line 823) | void mode11(uint16_t universe, uint16_t length, uint8_t sequence, uint8_...
function mode12 (line 858) | void mode12(uint16_t universe, uint16_t length, uint8_t sequence, uint8_...
function mode13 (line 909) | void mode13(uint16_t universe, uint16_t length, uint8_t sequence, uint8_...
function singleLed (line 982) | void singleLed(byte r, byte g, byte b, byte w) {
function singleRed (line 988) | void singleRed() {
function singleGreen (line 991) | void singleGreen() {
function singleBlue (line 994) | void singleBlue() {
function singleYellow (line 997) | void singleYellow() {
function singleCyan (line 1000) | void singleCyan() {
function singleMagenta (line 1003) | void singleMagenta() {
function red (line 1008) | uint8_t red(uint32_t c) {
function green (line 1011) | uint8_t green(uint32_t c) {
function blue (line 1014) | uint8_t blue(uint32_t c) {
function Wheel (line 1019) | uint32_t Wheel(byte WheelPos) {
function fullRed (line 1033) | void fullRed() {
function fullGreen (line 1041) | void fullGreen() {
function fullBlue (line 1048) | void fullBlue() {
function fullWhite (line 1055) | void fullWhite() {
function fullBlack (line 1062) | void fullBlack() {
function colorWipe (line 1070) | void colorWipe(uint8_t wait, uint32_t c) {
function pulseWhite (line 1078) | void pulseWhite(uint8_t wait) {
function rainbowFade2White (line 1096) | void rainbowFade2White(uint8_t wait, int rainbowLoops, int whiteLoops) {
function whiteOverRainbow (line 1144) | void whiteOverRainbow(uint8_t wait, uint8_t whiteSpeed, uint8_t whiteLen...
function rainbowCycle (line 1180) | void rainbowCycle(uint8_t wait) {
function rainbow (line 1191) | void rainbow(uint8_t wait) {
function map_hsv_to_rgb (line 1202) | void map_hsv_to_rgb(int *r, int *g, int *b) {
FILE: esp8266_artnet_neopixel/webinterface.cpp
function String (line 9) | static String getContentType(const String& path) {
function defaultConfig (line 31) | bool defaultConfig() {
function loadConfig (line 48) | bool loadConfig() {
function saveConfig (line 90) | bool saveConfig() {
function printRequest (line 120) | void printRequest() {
function handleUpdate1 (line 141) | void handleUpdate1() {
function handleUpdate2 (line 148) | void handleUpdate2() {
function handleDirList (line 173) | void handleDirList() {
function handleNotFound (line 186) | void handleNotFound() {
function handleRedirect (line 209) | void handleRedirect(const char * filename) {
function handleRedirect (line 213) | void handleRedirect(String filename) {
function handleStaticFile (line 220) | bool handleStaticFile(const char * path) {
function handleStaticFile (line 224) | bool handleStaticFile(String path) {
function handleJSON (line 238) | void handleJSON() {
FILE: esp8266_artnet_neopixel/webinterface.h
type Config (line 29) | struct Config {
FILE: esp8266_fieldtrip_buffer/fieldtrip_buffer.cpp
function fieldtrip_open_connection (line 11) | int fieldtrip_open_connection(const char *address, int port) {
function fieldtrip_close_connection (line 24) | int fieldtrip_close_connection(int s) {
function fieldtrip_write_header (line 33) | int fieldtrip_write_header(int server, uint32_t datatype, uint32_t nchan...
function fieldtrip_write_data (line 85) | int fieldtrip_write_data(int server, uint32_t datatype, uint32_t nchans,...
function wordsize_from_type (line 133) | int wordsize_from_type(uint32_t datatype) {
FILE: esp8266_fieldtrip_buffer/fieldtrip_buffer.h
type messagedef_t (line 61) | typedef struct {
type headerdef_t (line 69) | typedef struct {
type datadef_t (line 80) | typedef struct {
type eventdef_t (line 89) | typedef struct {
FILE: esp8266_imu_osc/I2Cscan.cpp
function I2Cscan (line 6) | void I2Cscan()
FILE: esp8266_imu_osc/rgb_led.cpp
function ledInit (line 3) | void ledInit() {
function ledRed (line 17) | void ledRed() {
function ledGreen (line 23) | void ledGreen() {
function ledBlue (line 29) | void ledBlue() {
function ledYellow (line 35) | void ledYellow() {
function ledMagenta (line 41) | void ledMagenta() {
function ledCyan (line 47) | void ledCyan() {
function ledBlack (line 53) | void ledBlack() {
function ledWhite (line 59) | void ledWhite() {
FILE: esp8266_imu_osc/tca9548a.h
function class (line 9) | class tca9548a {
FILE: esp8266_imu_osc/webinterface.cpp
function String (line 10) | static String getContentType(const String& path) {
function defaultConfig (line 32) | bool defaultConfig() {
function loadConfig (line 45) | bool loadConfig() {
function saveConfig (line 85) | bool saveConfig() {
function printRequest (line 113) | void printRequest() {
function handleUpdate1 (line 134) | void handleUpdate1() {
function handleUpdate2 (line 141) | void handleUpdate2() {
function handleDirList (line 166) | void handleDirList() {
function handleNotFound (line 179) | void handleNotFound() {
function handleRedirect (line 202) | void handleRedirect(const char * filename) {
function handleRedirect (line 206) | void handleRedirect(String filename) {
function handleStaticFile (line 213) | bool handleStaticFile(const char * path) {
function handleStaticFile (line 217) | bool handleStaticFile(String path) {
function handleJSON (line 231) | void handleJSON() {
function handleFileUpload (line 287) | void handleFileUpload() { // upload a new file to the SPIFFS
FILE: esp8266_imu_osc/webinterface.h
type Config (line 29) | struct Config {
FILE: esp8266_inmp411/RunningStat.h
function class (line 5) | class RunningStat
FILE: esp8266_polar_wearlink/rgb_led.cpp
function ledInit (line 3) | void ledInit() {
function ledRed (line 11) | void ledRed() {
function ledGreen (line 17) | void ledGreen() {
function ledBlue (line 23) | void ledBlue() {
function ledYellow (line 29) | void ledYellow() {
function ledMagenta (line 35) | void ledMagenta() {
function ledCyan (line 41) | void ledCyan() {
function ledBlack (line 47) | void ledBlack() {
function ledWhite (line 53) | void ledWhite() {
function ledRed (line 61) | void ledRed() {
function ledGreen (line 67) | void ledGreen() {
function ledBlue (line 73) | void ledBlue() {
function ledYellow (line 79) | void ledYellow() {
function ledMagenta (line 85) | void ledMagenta() {
function ledCyan (line 91) | void ledCyan() {
function ledBlack (line 97) | void ledBlack() {
function ledWhite (line 103) | void ledWhite() {
FILE: esp8266_polar_wearlink/webinterface.cpp
function String (line 10) | static String getContentType(const String& path) {
function defaultConfig (line 32) | bool defaultConfig() {
function loadConfig (line 41) | bool loadConfig() {
function saveConfig (line 75) | bool saveConfig() {
function printRequest (line 97) | void printRequest() {
function handleUpdate1 (line 118) | void handleUpdate1() {
function handleUpdate2 (line 125) | void handleUpdate2() {
function handleDirList (line 150) | void handleDirList() {
function handleNotFound (line 163) | void handleNotFound() {
function handleRedirect (line 186) | void handleRedirect(const char * filename) {
function handleRedirect (line 190) | void handleRedirect(String filename) {
function handleStaticFile (line 197) | bool handleStaticFile(const char * path) {
function handleStaticFile (line 201) | bool handleStaticFile(String path) {
function handleJSON (line 215) | void handleJSON() {
FILE: esp8266_polar_wearlink/webinterface.h
type Config (line 29) | struct Config {
FILE: esp8266_pulse_sensor/blink_led.cpp
function changeState (line 13) | void changeState() {
function ledInit (line 17) | void ledInit() {
function ledOn (line 21) | void ledOn() {
function ledOff (line 29) | void ledOff() {
function ledSlow (line 37) | void ledSlow() {
function ledMedium (line 45) | void ledMedium() {
function ledFast (line 53) | void ledFast() {
FILE: esp8266_pulse_sensor/fieldtrip_buffer.cpp
function fieldtrip_open_connection (line 11) | int fieldtrip_open_connection(const char *address, int port) {
function fieldtrip_close_connection (line 25) | int fieldtrip_close_connection(int s) {
function fieldtrip_write_header (line 37) | int fieldtrip_write_header(int server, uint32_t datatype, uint32_t nchan...
function fieldtrip_write_data (line 97) | int fieldtrip_write_data(int server, uint32_t datatype, uint32_t nchans,...
function wordsize_from_type (line 153) | int wordsize_from_type(uint32_t datatype) {
FILE: esp8266_pulse_sensor/fieldtrip_buffer.h
type messagedef_t (line 61) | typedef struct {
type headerdef_t (line 69) | typedef struct {
type datadef_t (line 80) | typedef struct {
type eventdef_t (line 89) | typedef struct {
FILE: esp8266_pulse_sensor/webinterface.cpp
function String (line 9) | static String getContentType(const String& path) {
function defaultConfig (line 31) | bool defaultConfig() {
function loadConfig (line 39) | bool loadConfig() {
function saveConfig (line 72) | bool saveConfig() {
function printRequest (line 94) | void printRequest() {
function handleUpdate1 (line 115) | void handleUpdate1() {
function handleUpdate2 (line 122) | void handleUpdate2() {
function handleDirList (line 147) | void handleDirList() {
function handleNotFound (line 160) | void handleNotFound() {
function handleRedirect (line 183) | void handleRedirect(const char * filename) {
function handleRedirect (line 187) | void handleRedirect(String filename) {
function handleStaticFile (line 194) | bool handleStaticFile(const char * path) {
function handleStaticFile (line 198) | bool handleStaticFile(String path) {
function handleJSON (line 212) | void handleJSON() {
FILE: esp8266_pulse_sensor/webinterface.h
type Config (line 29) | struct Config {
FILE: rp2040_dac7578/blink_led.cpp
function changeState (line 13) | void changeState() {
function ledInit (line 17) | void ledInit() {
function ledOn (line 21) | void ledOn() {
function ledOff (line 29) | void ledOff() {
function ledSlow (line 37) | void ledSlow() {
function ledMedium (line 45) | void ledMedium() {
function ledFast (line 53) | void ledFast() {
FILE: teensy_gps_temp_ttn/payload.h
type payload_t (line 3) | struct payload_t {
FILE: teensy_gps_temp_ttn/sensor.cpp
function init_sensor (line 39) | void init_sensor() {
function read_sensor (line 56) | void read_sensor() {
function readVcc (line 118) | uint32_t readVcc() {
function crc_update (line 134) | unsigned long crc_update(unsigned long crc, byte data)
function crc_buf (line 146) | unsigned long crc_buf(char *b, unsigned long l)
function crc_string (line 157) | unsigned long crc_string(char *s)
FILE: teensy_midifilter/midiname.c
type usb_string_descriptor_struct (line 15) | struct usb_string_descriptor_struct
FILE: uno_dac7578/blink_led.cpp
function ledInit (line 13) | void ledInit() {
function ledOn (line 18) | void ledOn() {
function ledOff (line 26) | void ledOff() {
function ledSlow (line 34) | void ledSlow() {
function ledMedium (line 42) | void ledMedium() {
function ledFast (line 50) | void ledFast() {
function ledFlip (line 58) | void ledFlip() {
Condensed preview — 263 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (694K chars).
[
{
"path": ".gitignore",
"chars": 33,
"preview": ".DS_Store\n*.swp\nsecret.h\n.vscode\n"
},
{
"path": ".gitmodules",
"chars": 131,
"preview": "[submodule \"esp8266_artnet_dmx512\"]\n\tpath = esp8266_artnet_dmx512\n\turl = git@github.com:robertoostenveld/esp8266_artnet_"
},
{
"path": "PulseSensor_v0/AllSerialHandling.cpp",
"chars": 2541,
"preview": "\n//////////\n///////// All Serial Handling Code, \n///////// It's Changeable with the 'serialVisual' variable\n///////// "
},
{
"path": "PulseSensor_v0/Interrupt.cpp",
"chars": 5744,
"preview": "\n\n\nvolatile int rate[10]; // array to hold last ten IBI values\nvolatile unsigned long sampleCounter ="
},
{
"path": "PulseSensor_v0/PulseSensor_v0.ino",
"chars": 3086,
"preview": "\n/* Pulse Sensor Amped 1.4 by Joel Murphy and Yury Gitman http://www.pulsesensor.com\n\n---------------------- Note"
},
{
"path": "PulseSensor_v0/README.md",
"chars": 236,
"preview": "# Arduino sketch for PulseSensor, see http://pulsesensor.com\n\nThis is the original code from https://github.com/WorldFam"
},
{
"path": "PulseSensor_v0/Timer_Interrupt_Notes.cpp",
"chars": 7678,
"preview": "/*\n These notes put together by Joel Murphy for Pulse Sensor Amped, 2015\n\n The code that this section is attached to u"
},
{
"path": "PulseSensor_v1/PulseSensor_v1.ino",
"chars": 1847,
"preview": "// PulseSensor application that streams continuous data at 500Hz using the OpenEEG data format\n// over the serial port o"
},
{
"path": "PulseSensor_v1/README.md",
"chars": 676,
"preview": "# Arduino sketch for PulseSensor, see http://pulsesensor.com\n\nThis reads the analog value of the sensor and sends it ove"
},
{
"path": "PulseSensor_v2/Interrupt.cpp",
"chars": 5588,
"preview": "\nvolatile int rate[10]; // array to hold last ten IBI values\nvolatile unsigned long lastBeatTime = "
},
{
"path": "PulseSensor_v2/PulseSensor_v2.ino",
"chars": 3014,
"preview": "#define LSB(x) (byte)(x & 0xff);\n#define MSB(x) (byte)(x>>8 & 0xff);\n\n#define pulsePin A0 // Pulse Se"
},
{
"path": "PulseSensor_v2/README.md",
"chars": 670,
"preview": "# Arduino sketch for PulseSensor, see http://pulsesensor.com\n\nThis code is derived from the [original code](https://gith"
},
{
"path": "README.md",
"chars": 1277,
"preview": "# Arduino electronics hacking\n\nMost of the code here corresponds to electronics hardware that is documented on my [perso"
},
{
"path": "bitsi/bitsi.ino",
"chars": 5959,
"preview": "/*\n * BITSI - Bits to Serial Interface\n *\n * This is an Arduino Uno based serial/usb interface that can be used to monit"
},
{
"path": "blenderdefender/blenderdefender.ino",
"chars": 5680,
"preview": "#include <JeeLib.h>\n#include <Ports.h>\n\n#define pirPin 9 // the number of the pushbutton pin\n#define redLed "
},
{
"path": "digispark_skateboard/README.md",
"chars": 1361,
"preview": "# Digispark Skateboard\n\nThis is an Arduino sketch to implement a strip of WS2812b Neopixels\nunder a skateboard. It featu"
},
{
"path": "digispark_skateboard/colorspace.cpp",
"chars": 2108,
"preview": "#include \"colorspace.h\"\n\nhsv rgb2hsv(rgb in)\n{\n hsv out;\n double min, max, delta;\n\n min = in.r < in.g ? "
},
{
"path": "digispark_skateboard/colorspace.h",
"chars": 324,
"preview": "#ifndef _COLORSPACE_H_\n#define _COLORSPACE_H_\n\ntypedef struct {\n double r; // percent\n double g; // percen"
},
{
"path": "digispark_skateboard/digispark_skateboard.ino",
"chars": 2198,
"preview": "#include <Arduino.h>\n#include \"neopixel_mode.h\"\n\n// the neopixel strip is connected to pin 0\n#define BUILTIN_LED 1\n#defi"
},
{
"path": "digispark_skateboard/neopixel_mode.cpp",
"chars": 3235,
"preview": "/*\n This is a subset of the color modes supported by my esp8266_artnet_neopixel sketch, see\n https://github.com/ro"
},
{
"path": "digispark_skateboard/neopixel_mode.h",
"chars": 925,
"preview": "#ifndef _NEOPIXEL_MODE_H_\n#define _NEOPIXEL_MODE_H_\n\n#include <Adafruit_NeoPixel.h>\n\n#define PIN 0\n#define NU"
},
{
"path": "eegsynth_cvgate_mcp4725/README.md",
"chars": 304,
"preview": "# Arduino sketch for an MCP4725 digital-to-analog converter\n\nThis is a sketch to control Control Voltages (CV) and Gates"
},
{
"path": "eegsynth_cvgate_mcp4725/eegsynth_cvgate_mcp4725.ino",
"chars": 6025,
"preview": "/*\n* EEGSynth Arduino based CV/Gate controller. This sketch allows\n* one to use control voltages and gates to interface "
},
{
"path": "eegsynth_cvgate_mcp4822/eegsynth_cvgate_mcp4822.ino",
"chars": 5927,
"preview": "/*\n* EEGSynth Arduino based CV/Gate controller. This sketch allows\n* one to use control voltages and gates to interface "
},
{
"path": "eegsynth_devirtualizer/eegsynth_devirtualizer.ino",
"chars": 8426,
"preview": "/*\n* EEGSynth Arduino based CV/Gate controller. This sketch allows\n* one to use control voltages and gates to interface "
},
{
"path": "eegsynth_usbdmxpro/COPYING.txt",
"chars": 1258,
"preview": "/*==============================================================================\n Copyright (c) 2013 Soixante circuits\n"
},
{
"path": "eegsynth_usbdmxpro/eegsynth_usbdmxpro.ino",
"chars": 3102,
"preview": "/*\n The purpose of this sketch is to implement a module that converts from USB to DMX512.\n This allows to use comput"
},
{
"path": "esp32_3wd_servo/README.md",
"chars": 1303,
"preview": "# Three-wheel-drive omni-wheel robot platform\n\nThis is an Arduino sketch for an ESP32 (LOLIN32 Lite) controlled\nthree-wh"
},
{
"path": "esp32_3wd_servo/esp32_3wd_servo.ino",
"chars": 4751,
"preview": "#include <Arduino.h>\n#include <WiFi.h>\n#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager\n#include <WiFiU"
},
{
"path": "esp32_3wd_stepper/README.md",
"chars": 3861,
"preview": "# Three-wheel-drive omni-wheel robot platform\n\nThis is an Arduino sketch for an ESP32 (LOLIN32 Lite) controlled three-wh"
},
{
"path": "esp32_3wd_stepper/blink_led.cpp",
"chars": 952,
"preview": "#include \"blink_led.h\"\n\nTicker blinker;\n\nenum {\n LED_ON,\n LED_OFF,\n LED_SLOW,\n LED_MEDIUM,\n LED_FAST,\n} ledState;\n\n"
},
{
"path": "esp32_3wd_stepper/blink_led.h",
"chars": 292,
"preview": "#ifndef _BLINK_LED_H_\n#define _BLINK_LED_H_\n\n#include <Arduino.h>\n#include <Ticker.h>\n\n#define LED 22 // GPIO22 is conne"
},
{
"path": "esp32_3wd_stepper/data/config.json",
"chars": 51,
"preview": "{\n\"repeat\":0,\n\"absolute\":0,\n\"warp\": 1,\n\"debug\":0\n}\n"
},
{
"path": "esp32_3wd_stepper/data/hello.html",
"chars": 221,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Hello</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp32_3wd_stepper/data/index.html",
"chars": 487,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Index</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp32_3wd_stepper/data/monitor.html",
"chars": 752,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Monitor</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_3wd_stepper/data/reload_failure.html",
"chars": 289,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Failure</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_3wd_stepper/data/reload_success.html",
"chars": 289,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Success</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_3wd_stepper/data/settings.html",
"chars": 1378,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Settings</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"style"
},
{
"path": "esp32_3wd_stepper/data/style.css",
"chars": 716,
"preview": ".c{\n text-align: center;\n}\ndiv,input{\n padding:5px;font-size:1em;\n}\ninput{\n width:95%;\n}\nbody{\n text-align: "
},
{
"path": "esp32_3wd_stepper/data/waypoints.html",
"chars": 2701,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Edit waypoints</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel="
},
{
"path": "esp32_3wd_stepper/data/waypoints1.csv",
"chars": 8,
"preview": "0,0,0,0\n"
},
{
"path": "esp32_3wd_stepper/data/waypoints2.csv",
"chars": 8,
"preview": "0,0,0,0\n"
},
{
"path": "esp32_3wd_stepper/data/waypoints3.csv",
"chars": 8,
"preview": "0,0,0,0\n"
},
{
"path": "esp32_3wd_stepper/data/waypoints4.csv",
"chars": 8,
"preview": "0,0,0,0\n"
},
{
"path": "esp32_3wd_stepper/data/waypoints5.csv",
"chars": 8,
"preview": "0,0,0,0\n"
},
{
"path": "esp32_3wd_stepper/data/waypoints6.csv",
"chars": 8,
"preview": "0,0,0,0\n"
},
{
"path": "esp32_3wd_stepper/data/waypoints7.csv",
"chars": 8,
"preview": "0,0,0,0\n"
},
{
"path": "esp32_3wd_stepper/data/waypoints8.csv",
"chars": 8,
"preview": "0,0,0,0\n"
},
{
"path": "esp32_3wd_stepper/esp32_3wd_stepper.ino",
"chars": 13832,
"preview": "#include <Arduino.h>\n#include <WiFi.h>\n#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager\n#include "
},
{
"path": "esp32_3wd_stepper/parseosc.cpp",
"chars": 6391,
"preview": "#include \"parseosc.h\"\n\n/********************************************************************************/\n\nvoid parseOSC"
},
{
"path": "esp32_3wd_stepper/parseosc.h",
"chars": 1133,
"preview": "#ifndef _PARSEOSC_H_\n#define _PARSEOSC_H_\n\n#include <WiFiUdp.h>\n#include <OSCMessage.h> // https://github.com/CN"
},
{
"path": "esp32_3wd_stepper/stepper.cpp",
"chars": 2962,
"preview": "#include \"stepper.h\"\n\n// This implements the ESP32 control for the 28BYJ-48 motor and the ULN2003 driver board.\n//\n// It"
},
{
"path": "esp32_3wd_stepper/stepper.h",
"chars": 879,
"preview": "#ifndef _STEPPER_H_\n#define _STEPPER_H_\n\n#include <Arduino.h>\n\nclass Stepper {\n public:\n Stepper();\n void begin(u"
},
{
"path": "esp32_3wd_stepper/util.cpp",
"chars": 1134,
"preview": "#include \"util.h\"\n\n/********************************************************************************/\n\nvoid printMacAddr"
},
{
"path": "esp32_3wd_stepper/util.h",
"chars": 212,
"preview": "#ifndef _UTIL_H_\n#define _UTIL_H_\n\n#include <Arduino.h>\n#include <WiFi.h>\n#include <esp_wifi.h>\n\nvoid printMacAddress(vo"
},
{
"path": "esp32_3wd_stepper/waypoints.cpp",
"chars": 3907,
"preview": "#include \"waypoints.h\"\n\n// read the sequence of waypoints from the CSV file on SPIFFS\n//\n// the file should have no head"
},
{
"path": "esp32_3wd_stepper/waypoints.h",
"chars": 814,
"preview": "#ifndef _WAYPOINTS_H_\n#define _WAYPOINTS_H_\n\n#include <FS.h>\n#include <SPIFFS.h>\n#include <vector>\n\nusing namespace std;"
},
{
"path": "esp32_3wd_stepper/webinterface.cpp",
"chars": 8131,
"preview": "#include \"webinterface.h\"\n#include \"waypoints.h\"\n\nConfig config;\nextern WebServer server;\n\n/****************************"
},
{
"path": "esp32_3wd_stepper/webinterface.h",
"chars": 1394,
"preview": "#ifndef _WEBINTERFACE_H_\n#define _WEBINTERFACE_H_\n\n#include <Arduino.h>\n#include <WebServer.h>\n#include <ArduinoJson.h> "
},
{
"path": "esp32_config_webinterface_v5/README.md",
"chars": 1966,
"preview": "# Overview\n\nThis is a demonstration and test sketch for configuring persistent options via the web interface. This strat"
},
{
"path": "esp32_config_webinterface_v5/data/config.json",
"chars": 47,
"preview": "{\n\"var1\":10,\n\"var2\":20,\n\"var3\":30,\n\"var4\":40\n}\n"
},
{
"path": "esp32_config_webinterface_v5/data/hello.html",
"chars": 215,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Hello</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp32_config_webinterface_v5/data/index.html",
"chars": 437,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Index</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp32_config_webinterface_v5/data/monitor.html",
"chars": 618,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Monitor</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_config_webinterface_v5/data/reload_failure.html",
"chars": 289,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Failure</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_config_webinterface_v5/data/reload_success.html",
"chars": 289,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Success</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_config_webinterface_v5/data/settings.html",
"chars": 1129,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Settings</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"style"
},
{
"path": "esp32_config_webinterface_v5/data/style.css",
"chars": 716,
"preview": ".c{\n text-align: center;\n}\ndiv,input{\n padding:5px;font-size:1em;\n}\ninput{\n width:95%;\n}\nbody{\n text-align: "
},
{
"path": "esp32_config_webinterface_v5/esp32_config_webinterface_v5.ino",
"chars": 9565,
"preview": "#if not defined(ESP32)\n#error This is a sketch for an ESP32 board, like the NodeMCU 32S, LOLIN32 Lite, or the Adafruit H"
},
{
"path": "esp32_config_webinterface_v6/README.md",
"chars": 1971,
"preview": "# Overview\n\nThis is a demonstration and test sketch for configuring persistent options via the web interface. This strat"
},
{
"path": "esp32_config_webinterface_v6/data/config.json",
"chars": 36,
"preview": "{\n\"var1\":10,\n\"var2\":20,\n\"var3\":30\n}\n"
},
{
"path": "esp32_config_webinterface_v6/data/hello.html",
"chars": 215,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Hello</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp32_config_webinterface_v6/data/index.html",
"chars": 437,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Index</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp32_config_webinterface_v6/data/monitor.html",
"chars": 618,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Monitor</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_config_webinterface_v6/data/reload_failure.html",
"chars": 289,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Failure</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_config_webinterface_v6/data/reload_success.html",
"chars": 289,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Success</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_config_webinterface_v6/data/settings.html",
"chars": 1129,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Settings</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"style"
},
{
"path": "esp32_config_webinterface_v6/data/style.css",
"chars": 716,
"preview": ".c{\n text-align: center;\n}\ndiv,input{\n padding:5px;font-size:1em;\n}\ninput{\n width:95%;\n}\nbody{\n text-align: "
},
{
"path": "esp32_config_webinterface_v6/esp32_config_webinterface_v6.ino",
"chars": 3225,
"preview": "#if not defined(ESP32)\n#error This is a sketch for an ESP32 board, like the NodeMCU 32S, LOLIN32 Lite, or the Adafruit H"
},
{
"path": "esp32_config_webinterface_v6/webinterface.cpp",
"chars": 6275,
"preview": "#include \"webinterface.h\"\n\nConfig config;\nextern WebServer server;\n\n/***************************************************"
},
{
"path": "esp32_config_webinterface_v6/webinterface.h",
"chars": 1373,
"preview": "#ifndef _WEBINTERFACE_H_\n#define _WEBINTERFACE_H_\n\n#include <Arduino.h>\n#include <WebServer.h>\n#include <ArduinoJson.h> "
},
{
"path": "esp32_config_webinterface_v7/README.md",
"chars": 1971,
"preview": "# Overview\n\nThis is a demonstration and test sketch for configuring persistent options via the web interface. This strat"
},
{
"path": "esp32_config_webinterface_v7/data/config.json",
"chars": 36,
"preview": "{\n\"var1\":10,\n\"var2\":20,\n\"var3\":30\n}\n"
},
{
"path": "esp32_config_webinterface_v7/data/hello.html",
"chars": 215,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Hello</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp32_config_webinterface_v7/data/index.html",
"chars": 437,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Index</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp32_config_webinterface_v7/data/monitor.html",
"chars": 618,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Monitor</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_config_webinterface_v7/data/reload_failure.html",
"chars": 289,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Failure</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_config_webinterface_v7/data/reload_success.html",
"chars": 289,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Success</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_config_webinterface_v7/data/settings.html",
"chars": 1129,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Settings</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"style"
},
{
"path": "esp32_config_webinterface_v7/data/style.css",
"chars": 716,
"preview": ".c{\n text-align: center;\n}\ndiv,input{\n padding:5px;font-size:1em;\n}\ninput{\n width:95%;\n}\nbody{\n text-align: "
},
{
"path": "esp32_config_webinterface_v7/esp32_config_webinterface_v7.ino",
"chars": 3213,
"preview": "#if not defined(ESP32)\n#error This is a sketch for an ESP32 board, like the NodeMCU 32S, LOLIN32 Lite, or the Adafruit H"
},
{
"path": "esp32_config_webinterface_v7/webinterface.cpp",
"chars": 6200,
"preview": "#include \"webinterface.h\"\n\nConfig config;\nextern WebServer server;\n\n/***************************************************"
},
{
"path": "esp32_config_webinterface_v7/webinterface.h",
"chars": 1373,
"preview": "#ifndef _WEBINTERFACE_H_\n#define _WEBINTERFACE_H_\n\n#include <Arduino.h>\n#include <WebServer.h>\n#include <ArduinoJson.h> "
},
{
"path": "esp32_exgpill/README.md",
"chars": 1200,
"preview": "# Overview\n\nThis is an Arduino sketch for an ExGpill connected to a LOLIN32 Lite development board. It features a webint"
},
{
"path": "esp32_exgpill/data/config.json",
"chars": 42,
"preview": "{\n\"address\":\"192.168.1.34\",\n\"port\":1972\n}\n"
},
{
"path": "esp32_exgpill/data/hello.html",
"chars": 225,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Hello</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp32_exgpill/data/index.html",
"chars": 437,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Index</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp32_exgpill/data/monitor.html",
"chars": 740,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Monitor</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_exgpill/data/reload_failure.html",
"chars": 289,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Failure</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_exgpill/data/reload_success.html",
"chars": 289,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Success</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp32_exgpill/data/settings.html",
"chars": 944,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Settings</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"style"
},
{
"path": "esp32_exgpill/data/style.css",
"chars": 716,
"preview": ".c{\n text-align: center;\n}\ndiv,input{\n padding:5px;font-size:1em;\n}\ninput{\n width:95%;\n}\nbody{\n text-align: "
},
{
"path": "esp32_exgpill/esp32_exgpill.ino",
"chars": 6113,
"preview": "#if not defined(ESP32)\n#error This is a sketch for an ESP32 board, like the NodeMCU 32S, LOLIN32 Lite, or the Adafruit H"
},
{
"path": "esp32_exgpill/fieldtrip_buffer.cpp",
"chars": 4505,
"preview": "#include \"fieldtrip_buffer.h\"\n\nWiFiClient client;\n\n#undef DEBUG\n\n/******************************************************"
},
{
"path": "esp32_exgpill/fieldtrip_buffer.h",
"chars": 3845,
"preview": "#ifndef __BUFFER_H_\n#define __BUFFER_H_\n\n#include <WiFiClient.h>\n\n// definition of simplified interface functions, not a"
},
{
"path": "esp32_exgpill/rgb_led.cpp",
"chars": 3002,
"preview": "Yellow#include \"rgb_led.h\"\n\nTicker blinker;\n\n#ifdef COMMON_ANODE\n #define LED_ON LOW\n #define LED_OFF HIGH\n#else\n #d"
},
{
"path": "esp32_exgpill/rgb_led.h",
"chars": 824,
"preview": "#ifndef _RGB_LED_H_\n#define _RGB_LED_H_\n\n#include <Arduino.h>\n#include <Ticker.h>\n\n// #define COMMON_ANODE\n#define LED_R"
},
{
"path": "esp32_exgpill/webinterface.cpp",
"chars": 6403,
"preview": "#include <WebServer.h>\n#include <Arduino.h>\n#include <ArduinoJson.h>\n#include <FS.h>\n#include <SPIFFS.h>\n\n#include \"webi"
},
{
"path": "esp32_exgpill/webinterface.h",
"chars": 434,
"preview": "#ifndef _WEBINTERFACE_H_\n#define _WEBINTERFACE_H_\n\nstruct Config {\n char address[32];\n int port;\n};\n\nextern Config con"
},
{
"path": "esp32_inmp441/RunningStat.h",
"chars": 1692,
"preview": "/*\n See https://www.johndcook.com/blog/standard_deviation/ and https://www.johndcook.com/blog/skewness_kurtosis/\n*/\n\n#"
},
{
"path": "esp32_inmp441/esp32_inmp441.ino",
"chars": 9254,
"preview": "/*\n Sketch for an ESP32 board, like the NodeMCU 32S, LOLIN32 Lite, or the Adafruit Huzzah32\n connected to a INMP41"
},
{
"path": "esp32_inmp441/util.h",
"chars": 539,
"preview": "#ifndef _UTIL_H_\n#define _UTIL_H_\n\nvoid print_binary(uint32_t val) {\n for (int l = 31; l >= 24; l--) {\n uint8_t b = "
},
{
"path": "esp32_sph0645/RunningStat.h",
"chars": 1263,
"preview": "/*\n See https://www.johndcook.com/blog/standard_deviation/\n*/\n\nclass RunningStat\n{\n public:\n RunningStat() : m_n(0"
},
{
"path": "esp32_sph0645/compress.cpp",
"chars": 504,
"preview": "#include <Arduino.h>\n\nuint32_t compress(uint32_t *data, uint32_t nsamples, uint8_t *dest) {\n uint32_t nbytes = 0;\n uin"
},
{
"path": "esp32_sph0645/esp32_sph0645.ino",
"chars": 8250,
"preview": "/*\n Sketch for ESP32 board, like the NodeMCU 32S, LOLIN32 Lite, or the Adafruit Huzzah32\n connected to a SPH0645 I2S"
},
{
"path": "esp32c6_feeder/README.md",
"chars": 681,
"preview": "# Pet feeder\n\nThis Arduino sketch is for a battery operated pet feeder based on a XIAO ESP32c6. It uses deep \nsleep to c"
},
{
"path": "esp32c6_feeder/esp32c6_feeder.ino",
"chars": 4345,
"preview": "/* \nThis Arduino sketch is for a battery operated pet feeder based on a XIAO ESP32c6. It uses deep \nsleep to conserve po"
},
{
"path": "esp8266_12v_trigger/esp8266_12v_trigger.ino",
"chars": 3991,
"preview": "/*\n * This sketch serves as a 12 volt trigger to switch a NAD-D3020 audio amplifier on and off.\n * The on and off stat"
},
{
"path": "esp8266_ad8232_ecg/README.md",
"chars": 1281,
"preview": "# Overview\n\nThis is an Arduino sketch for an ESP8266 module connected to an AD8232 ECG sensor module. It uses the ADC to"
},
{
"path": "esp8266_ad8232_ecg/blink_led.cpp",
"chars": 952,
"preview": "#include \"blink_led.h\"\n\nTicker blinker;\n\nenum {\n LED_ON,\n LED_OFF,\n LED_SLOW,\n LED_MEDIUM,\n LED_FAST,\n} ledState;\n\n"
},
{
"path": "esp8266_ad8232_ecg/blink_led.h",
"chars": 270,
"preview": "#ifndef _BLINK_LED_H_\n#define _BLINK_LED_H_\n\n#include <Arduino.h>\n#include <Ticker.h>\n\n#define LED 2 // GPIO2 is connect"
},
{
"path": "esp8266_ad8232_ecg/data/config.json",
"chars": 48,
"preview": "{\n \"address\": \"192.168.1.34\",\n \"port\": 1972\n}\n"
},
{
"path": "esp8266_ad8232_ecg/data/hello.html",
"chars": 215,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Hello</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp8266_ad8232_ecg/data/index.html",
"chars": 485,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Index</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp8266_ad8232_ecg/data/monitor.html",
"chars": 618,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Monitor</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp8266_ad8232_ecg/data/reload_failure.html",
"chars": 290,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Failure</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp8266_ad8232_ecg/data/reload_success.html",
"chars": 290,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Success</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp8266_ad8232_ecg/data/settings.html",
"chars": 945,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Settings</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"style"
},
{
"path": "esp8266_ad8232_ecg/data/style.css",
"chars": 716,
"preview": ".c{\n text-align: center;\n}\ndiv,input{\n padding:5px;font-size:1em;\n}\ninput{\n width:85%;\n}\nbody{\n text-align: "
},
{
"path": "esp8266_ad8232_ecg/data/update.html",
"chars": 417,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Update</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"stylesh"
},
{
"path": "esp8266_ad8232_ecg/esp8266_ad8232_ecg.ino",
"chars": 7562,
"preview": "/*\n This sketch is for an ESP8266 connected to an AD8232 module to record the ECG\n and to send the continuous signal w"
},
{
"path": "esp8266_ad8232_ecg/fieldtrip_buffer.cpp",
"chars": 4894,
"preview": "#include \"fieldtrip_buffer.h\"\n\nWiFiClient client;\n\n#undef DEBUG\n\n/******************************************************"
},
{
"path": "esp8266_ad8232_ecg/fieldtrip_buffer.h",
"chars": 3845,
"preview": "#ifndef __BUFFER_H_\n#define __BUFFER_H_\n\n#include <WiFiClient.h>\n\n// definition of simplified interface functions, not a"
},
{
"path": "esp8266_ad8232_ecg/webinterface.cpp",
"chars": 7557,
"preview": "#include \"webinterface.h\"\n#include \"blink_led.h\"\n\nConfig config;\nextern ESP8266WebServer server;\n\n/*********************"
},
{
"path": "esp8266_ad8232_ecg/webinterface.h",
"chars": 1406,
"preview": "#ifndef _WEBINTERFACE_H_\n#define _WEBINTERFACE_H_\n\n#include <Arduino.h>\n#include <ArduinoJson.h>\n#include <string.h>\n#in"
},
{
"path": "esp8266_artnet_bci/esp8266_artnet_bci.ino",
"chars": 8684,
"preview": "#include <ESP8266WiFi.h> // https://github.com/esp8266/Arduino\n#include <ESP8266WebServer.h>\n#include <ESP8266mD"
},
{
"path": "esp8266_artnet_neopixel/README.md",
"chars": 6412,
"preview": "# Overview\n\nThis is an Arduino sketch for an ESP8266 module connected to a neopixel LED strip. It uses ArtNet to control"
},
{
"path": "esp8266_artnet_neopixel/colorspace.cpp",
"chars": 2108,
"preview": "#include \"colorspace.h\"\n\nhsv rgb2hsv(rgb in)\n{\n hsv out;\n double min, max, delta;\n\n min = in.r < in.g ? "
},
{
"path": "esp8266_artnet_neopixel/colorspace.h",
"chars": 337,
"preview": "#ifndef _COLORSPACE_H_\n#define _COLORSPACE_H_\n\ntypedef struct {\n double r; // percent\n double g; // pe"
},
{
"path": "esp8266_artnet_neopixel/data/hello.html",
"chars": 213,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Hello</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp8266_artnet_neopixel/data/index.html",
"chars": 485,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Index</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp8266_artnet_neopixel/data/monitor.html",
"chars": 859,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Monitor</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp8266_artnet_neopixel/data/reload_failure.html",
"chars": 290,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Failure</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp8266_artnet_neopixel/data/reload_success.html",
"chars": 290,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Success</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp8266_artnet_neopixel/data/settings.html",
"chars": 2366,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Settings</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"style"
},
{
"path": "esp8266_artnet_neopixel/data/style.css",
"chars": 716,
"preview": ".c{\n text-align: center;\n}\ndiv,input{\n padding:5px;font-size:1em;\n}\ninput{\n width:85%;\n}\nbody{\n text-align: "
},
{
"path": "esp8266_artnet_neopixel/data/update.html",
"chars": 417,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Update</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"stylesh"
},
{
"path": "esp8266_artnet_neopixel/esp8266_artnet_neopixel.ino",
"chars": 7357,
"preview": "/*\n This sketch receive a DMX universes via Artnet to control a\n strip of ws2811 leds via Adafruit's NeoPixel library:"
},
{
"path": "esp8266_artnet_neopixel/font8x8_basic.h",
"chars": 9808,
"preview": "/** \n * 8x8 monochrome bitmap fonts for rendering\n * Author: Daniel Hepper <daniel@hepper.net>\n * \n * Modified from http"
},
{
"path": "esp8266_artnet_neopixel/neopixel_mode.cpp",
"chars": 35877,
"preview": "#include \"neopixel_mode.h\"\n#include \"webinterface.h\"\n#include \"colorspace.h\"\n#include \"font8x8_basic.h\"\n\nextern Adafruit"
},
{
"path": "esp8266_artnet_neopixel/neopixel_mode.h",
"chars": 4259,
"preview": "#ifndef _MODE_H_\n#define _MODE_H_\n\n#include <Arduino.h>\n#include <ESP8266WiFi.h>\n#include <WiFiUdp.h>\n#include <ArtnetWi"
},
{
"path": "esp8266_artnet_neopixel/webinterface.cpp",
"chars": 9186,
"preview": "#include \"webinterface.h\"\n\nConfig config;\nextern ESP8266WebServer server;\nextern int packetCounter;\n\n/******************"
},
{
"path": "esp8266_artnet_neopixel/webinterface.h",
"chars": 1545,
"preview": "#ifndef _WEBINTERFACE_H_\n#define _WEBINTERFACE_H_\n\n#include <Arduino.h>\n#include <ArduinoJson.h>\n#include <string.h>\n#in"
},
{
"path": "esp8266_config_spiffs/data/config.json",
"chars": 36,
"preview": "{\n\"var1\":10,\n\"var2\":20,\n\"var3\":30\n}\n"
},
{
"path": "esp8266_config_spiffs/esp8266_config_spiffs.ino",
"chars": 2403,
"preview": "#include <Arduino.h>\n#include <ArduinoJson.h>\n#include <FS.h>\n\n#if defined(ESP32)\n#include <SPIFFS.h>\n#endif\n\n#ifndef AR"
},
{
"path": "esp8266_config_webinterface/README.md",
"chars": 1986,
"preview": "# Overview\n\nThis is a demonstration and test sketch for configuring persistent options via the web interface. This strat"
},
{
"path": "esp8266_config_webinterface/data/config.json",
"chars": 47,
"preview": "{\n\"var1\":10,\n\"var2\":20,\n\"var3\":30,\n\"var4\":40\n}\n"
},
{
"path": "esp8266_config_webinterface/data/hello.html",
"chars": 213,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Hello</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp8266_config_webinterface/data/index.html",
"chars": 485,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Index</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp8266_config_webinterface/data/monitor.html",
"chars": 618,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Monitor</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp8266_config_webinterface/data/reload_failure.html",
"chars": 289,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Failure</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp8266_config_webinterface/data/reload_success.html",
"chars": 289,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Success</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp8266_config_webinterface/data/settings.html",
"chars": 1129,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Settings</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"style"
},
{
"path": "esp8266_config_webinterface/data/style.css",
"chars": 716,
"preview": ".c{\n text-align: center;\n}\ndiv,input{\n padding:5px;font-size:1em;\n}\ninput{\n width:95%;\n}\nbody{\n text-align: "
},
{
"path": "esp8266_config_webinterface/data/update.html",
"chars": 416,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Update</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"stylesh"
},
{
"path": "esp8266_config_webinterface/esp8266_config_webinterface.ino",
"chars": 10489,
"preview": "\n#include <Arduino.h>\n#include <ArduinoJson.h> // https://arduinojson.org\n#include <ESP8266WiFi.h> // "
},
{
"path": "esp8266_fan_control/esp8266_fan_control.ino",
"chars": 1391,
"preview": "/*\n This sketch is for an ESP8266 connected to an ARCTIC BioniX F140\n 140 mm diameter fan with PWM control.\n\n See htt"
},
{
"path": "esp8266_fieldtrip_buffer/esp8266_fieldtrip_buffer.ino",
"chars": 2083,
"preview": "#include <ESP8266WiFi.h>\n\n#include \"secret.h\"\n#include \"fieldtrip_buffer.h\"\n\nint ftserver = 0, status = 0;\n\n#define NCHA"
},
{
"path": "esp8266_fieldtrip_buffer/fieldtrip_buffer.cpp",
"chars": 4505,
"preview": "#include \"fieldtrip_buffer.h\"\n\nWiFiClient client;\n\n#undef DEBUG\n\n/******************************************************"
},
{
"path": "esp8266_fieldtrip_buffer/fieldtrip_buffer.h",
"chars": 3845,
"preview": "#ifndef __BUFFER_H_\n#define __BUFFER_H_\n\n#include <WiFiClient.h>\n\n// definition of simplified interface functions, not a"
},
{
"path": "esp8266_imu_osc/I2Cscan.cpp",
"chars": 964,
"preview": "#include <Arduino.h>\n#include <Wire.h>\n\n#include \"I2Cscan.h\"\n\nvoid I2Cscan()\n{\n // scan for i2c devices\n byte error, a"
},
{
"path": "esp8266_imu_osc/I2Cscan.h",
"chars": 69,
"preview": "#ifndef __I2CSCAN_H__\n#define __I2CSCAN_H__\n\nvoid I2Cscan();\n\n#endif\n"
},
{
"path": "esp8266_imu_osc/README.md",
"chars": 1273,
"preview": "# Overview\n\nThis is an Arduino sketch for an ESP8266 module that is connected to a number of inertial motion units (IMUs"
},
{
"path": "esp8266_imu_osc/data/config.json",
"chars": 159,
"preview": "{\n\t\"sensors\": 8,\n\t\"decimate\": 1,\n\t\"calibrate\": 0,\n\t\"raw\": 1,\n\t\"ahrs\": 0,\n\t\"quaternion\": 0,\n\t\"termperature\": 0,\n\t\"destina"
},
{
"path": "esp8266_imu_osc/data/hello.html",
"chars": 217,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Hello</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp8266_imu_osc/data/index.html",
"chars": 485,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Index</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp8266_imu_osc/data/monitor.html",
"chars": 729,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Monitor</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp8266_imu_osc/data/reload_failure.html",
"chars": 290,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Failure</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp8266_imu_osc/data/reload_success.html",
"chars": 290,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Success</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp8266_imu_osc/data/settings.html",
"chars": 2124,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Settings</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"style"
},
{
"path": "esp8266_imu_osc/data/style.css",
"chars": 716,
"preview": ".c{\n text-align: center;\n}\ndiv,input{\n padding:5px;font-size:1em;\n}\ninput{\n width:85%;\n}\nbody{\n text-align: "
},
{
"path": "esp8266_imu_osc/data/update.html",
"chars": 417,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Update</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"stylesh"
},
{
"path": "esp8266_imu_osc/esp8266_imu_osc.ino",
"chars": 20755,
"preview": "/*\n This sketch is for an ESP8266 connected to an TCA9548A I2C multiplexer,\n which in turn connects to multiple MPU925"
},
{
"path": "esp8266_imu_osc/rgb_led.cpp",
"chars": 1166,
"preview": "#include \"rgb_led.h\"\n\nvoid ledInit() {\n pinMode(LED_R, OUTPUT);\n pinMode(LED_G, OUTPUT);\n pinMode(LED_B, OUTPUT);\n}\n\n"
},
{
"path": "esp8266_imu_osc/rgb_led.h",
"chars": 299,
"preview": "#ifndef _RGB_LED_H_\n#define _RGB_LED_H_\n\n#include <Arduino.h>\n\n#define LED_R D5\n#define LED_G D6\n#define LED_B D7\n\n// #d"
},
{
"path": "esp8266_imu_osc/tca9548a.cpp",
"chars": 398,
"preview": "#include \"tca9548a.h\"\n\ntca9548a::tca9548a(void) {\n}\n\ntca9548a::~tca9548a(void) {\n}\n\nvoid tca9548a::select(uint8_t channe"
},
{
"path": "esp8266_imu_osc/tca9548a.h",
"chars": 287,
"preview": "#ifndef __TCA9548A_H_\n#define __TCA9548A_H_\n\n#include <Arduino.h>\n#include <Wire.h>\n\n#define TCA9548A_ADDRESS 0x70\n\nc"
},
{
"path": "esp8266_imu_osc/webinterface.cpp",
"chars": 10412,
"preview": "#include \"webinterface.h\"\n#include \"rgb_led.h\"\n\nConfig config;\nextern ESP8266WebServer server;\nextern unsigned long pack"
},
{
"path": "esp8266_imu_osc/webinterface.h",
"chars": 1624,
"preview": "#ifndef _WEBINTERFACE_H_\n#define _WEBINTERFACE_H_\n\n#include <Arduino.h>\n#include <ArduinoJson.h>\n#include <string.h>\n#in"
},
{
"path": "esp8266_inmp411/RunningStat.h",
"chars": 1263,
"preview": "/*\n See https://www.johndcook.com/blog/standard_deviation/\n*/\n\nclass RunningStat\n{\n public:\n RunningStat() : m_n(0"
},
{
"path": "esp8266_inmp411/esp8266_inmp411.ino",
"chars": 4586,
"preview": "/*\n Sketch for ESP8266 board, like the WEMOS D1 mini pro\n connected to a INMP411 I2S microphone\n\n The *internal GP"
},
{
"path": "esp8266_p1_thingspeak/esp8266_p1_thingspeak.ino",
"chars": 13214,
"preview": "/*\n This sketch is designed for the P1 port of an ISKRA AM500 smart energy meter.\n It runs on a Wemos D1 mini PRO th"
},
{
"path": "esp8266_polar_wearlink/README.md",
"chars": 1071,
"preview": "# Overview\n\nThis is designed to link https://www.adafruit.com/product/1077 to http://www.eegsynth.org\n\n## SPIFFS for sta"
},
{
"path": "esp8266_polar_wearlink/data/config.json",
"chars": 62,
"preview": "{\n\t\"redis\": \"192.168.1.34\",\n\t\"port\": 6379,\n\t\"duration\": 100\n}\n"
},
{
"path": "esp8266_polar_wearlink/data/hello.html",
"chars": 221,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Hello</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp8266_polar_wearlink/data/index.html",
"chars": 485,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Index</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styleshe"
},
{
"path": "esp8266_polar_wearlink/data/monitor.html",
"chars": 748,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Monitor</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp8266_polar_wearlink/data/reload_failure.html",
"chars": 290,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Failure</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
},
{
"path": "esp8266_polar_wearlink/data/reload_success.html",
"chars": 290,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n<title>Success</title>\n<meta charset=\"utf-8\">\n<link href=\"style.css\" rel=\"styles"
}
]
// ... and 63 more files (download for full content)
About this extraction
This page contains the full source code of the robertoostenveld/arduino GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 263 files (631.7 KB), approximately 198.0k tokens, and a symbol index with 334 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.