Created
March 18, 2024 12:33
-
-
Save jaggzh/0080773fecedcd27339e0a0ec6e55fd0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdint.h> | |
#include <cfloat> // FLT_MIN FLT_MAX | |
#include "driver/adc.h" // use esp-idf directly | |
#define OPT_PLOT_DATA | |
/* #define OPT_PLOT_COUNT */ | |
#define US_PER_SAMP 45 | |
#define US_PER_60HZ_CYCLE 8333 | |
/* #define BS 120 */ | |
#define BS 150 | |
#define CYCLES 30 | |
#define CHOP BS | |
#define TRIGGER_FRAC .90 | |
// Define macros for simplified serial printing | |
#define sp(v) Serial.print(v) | |
#define spl(v) Serial.println(v) | |
#define spt(v) do { Serial.print(v); Serial.print('\t'); } while(0) | |
#define spnl() Serial.println("") | |
// Define pin for sensing | |
#define IN1PIN 32 | |
#define __min(a,b) ((a)<(b)?(a):(b)) | |
#define __max(a,b) ((a)>(b)?(a):(b)) | |
// State machine states | |
enum State { | |
ST_START, | |
ST_HIGHWAIT, | |
/* ST_READ, */ | |
ST_PROCESSING, | |
ST_LOWWAIT | |
}; | |
// Timing and state management variables | |
unsigned long highwait_delay_us = 0; | |
unsigned long lowwait_delay_us = 5000; | |
unsigned long hightime_us = 0; | |
unsigned long lowtime_us = 0; | |
int16_t in1zeros = 0; | |
int16_t in1ones = 0; | |
int16_t in1ctr = 0; // Additional counter for tracking the number of processed events | |
// Calibration thresholds | |
#define MIN_RATIO_CTR 100 | |
#define MAX_RATIO_CTR (INT16_MAX / 2 - 2) | |
#define RATIO_CTR_RESET_VAL (MAX_RATIO_CTR - MIN_RATIO_CTR) | |
State state = ST_START; | |
float maxv=FLT_MIN, minv=FLT_MAX; | |
float smoothv, slowv, basev; | |
float lastv; | |
/* OnePinCapSense opcs = OnePinCapSense(); */ | |
int buf_v[BS+1]; | |
float buf_mavg_div[BS+1]; | |
void read_cycle(void) { | |
for (int i=0; i<BS; i++) { | |
buf_v[i] = adc1_get_raw(ADC1_CHANNEL_4); // twice as fast | |
} | |
} | |
void read_cycle_buf(int *b, int len) { | |
for (int i=0; i<len; i++) { | |
b[i] = adc1_get_raw(ADC1_CHANNEL_4); // twice as fast | |
} | |
} | |
#define copy_cycle(d,f,len) memcpy(d, f, len*sizeof(*f)) | |
unsigned long us_rising_4isr_st=0; | |
unsigned long us_rising_4isr_en=0; | |
void fisr_rising() { us_rising_4isr_en=micros(); } | |
void setup() { | |
Serial.begin(230400); | |
delay(2000); // safety so easier to flash if crashing code | |
// adc currently not unused v v v v | |
adc1_config_width(ADC_WIDTH_BIT_11); // Set ADC resolution to 10 bits | |
adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11); // Example configuration | |
pinMode(IN1PIN, INPUT); | |
smoothv = lastv = slowv = basev = adc1_get_raw(ADC1_CHANNEL_4); | |
pinMode(IN1PIN, OUTPUT); | |
digitalWrite(IN1PIN, LOW); | |
state = ST_START; | |
attachInterrupt(IN1PIN, fisr_rising, RISING); | |
delay(50); | |
/* read_cycle(); */ | |
/* copy_cycle(buf_mavg_div, buf_v, BS); */ | |
} | |
void merge_cycle_mavg(float *db, float *newb, int div, unsigned int len) { | |
// merge difference with divider | |
db[0] = newb[0]; | |
for (int i=1; i<len; i++) { | |
db[i] += (newb[i] - db[i])/div; | |
} | |
} | |
void merge_cycle_mavg(float *db, int *newb, int div, unsigned int len) { | |
// merge difference with divider | |
db[0] = newb[0]; | |
for (int i=1; i<len; i++) { | |
db[i] += (newb[i] - db[i])/div; | |
} | |
} | |
void get_minmax(float *mnstore, float *mxstore, float *b, int len) { | |
float mn = FLT_MAX; | |
float mx = FLT_MIN; | |
for (int i=0; i<len; i++) { | |
if (b[i] > mx) mx = b[i]; | |
if (b[i] < mn) mn = b[i]; | |
} | |
*mnstore = mn; | |
*mxstore = mx; | |
} | |
int find_cnt_at_thresh(float *b, int len, float frac, float mn, float mx) { | |
float thresh=mn + (mx-mn)*frac; | |
for (int i=0; i<len; i++) { | |
if (b[i] > thresh) return i; | |
} | |
return -1; | |
} | |
void smooth_buf(float *dest, int *src, int len, int window, bool repeat_boundary) { | |
// Check if the window size is odd, adjust if even to ensure symmetry for smoothing | |
if (window % 2 == 0) { | |
window++; // Make window size odd if it's even | |
} | |
int halfWindow = window / 2; | |
for (int i = 0; i < len; i++) { | |
float sum = 0; | |
int count = 0; | |
for (int j = -halfWindow; j <= halfWindow; j++) { | |
int idx = i + j; | |
// Handle boundary cases | |
if (repeat_boundary) { | |
// Repeat boundary values if outside the array | |
if (idx < 0) idx = 0; | |
else if (idx >= len) idx = len - 1; | |
} else { | |
// Skip the out-of-bounds indices | |
if (idx < 0 || idx >= len) continue; | |
} | |
sum += src[idx]; | |
count++; | |
} | |
dest[i] = sum / count; // Compute the average and assign it to the dest array | |
} | |
} | |
// https://gist.github.com/jaggzh/552022494eff43cf2a3712931d315c4d | |
void loop() { | |
unsigned long cmicros = micros(); | |
unsigned long cmillis = millis(); | |
unsigned long endmicros = micros(); | |
static unsigned long last_read = cmillis; | |
static unsigned long last_print = cmillis; | |
unsigned long buf_us[BS+1]; | |
float curmin, curmax; | |
float buf_v_smooth[BS]; | |
/* #define TEST_TIMING_60HZ */ | |
#ifdef TEST_TIMING_60HZ | |
if (cmillis - last_read > 50) { | |
#define SS4 185*8 | |
int bb[SS4]; | |
pinMode(IN1PIN, INPUT); | |
read_cycle_buf(bb, SS4); | |
int minVal = bb[0]; | |
int maxVal = bb[0]; | |
for (int i = 1; i < SS4; i++) { | |
if (bb[i] < minVal) minVal = bb[i]; | |
if (bb[i] > maxVal) maxVal = bb[i]; | |
} | |
int midpoint = (minVal + maxVal) / 2; | |
int zeroup = -1; // Initialize with -1 to indicate "not found" | |
// Search for the first value crossing the midpoint upwards | |
for (int i = 0; i < SS4 - 1; i++) { | |
if (bb[i] <= midpoint && bb[i + 1] > midpoint) { | |
zeroup = i + 1; // +1 to mark the transition point | |
break; // Exit the loop once the first midpoint crossing is found | |
} | |
} | |
if (zeroup != -1) { | |
// Limit the output to a quarter of the buffer size from the zero crossing point | |
for (int i = zeroup; i < SS4 && i - zeroup < SS4 / 4; i+=4) { | |
sp("z:"); | |
spl(bb[i]); | |
} | |
} | |
last_read = millis(); | |
} | |
return; | |
#endif | |
#define TEST_TIMING_ISR | |
#ifdef TEST_TIMING_ISR | |
static float slow_rise_us=100; // just somewhere to start | |
unsigned long cur_rise_us; | |
if (state == ST_START) { | |
if (cmillis-last_read > 5) { | |
pinMode(IN1PIN, OUTPUT); | |
digitalWrite(IN1PIN, LOW); | |
delayMicroseconds(12); | |
state = ST_HIGHWAIT; | |
us_rising_4isr_en = 0; | |
us_rising_4isr_st = micros(); | |
pinMode(IN1PIN, INPUT); | |
} | |
} else if (state == ST_HIGHWAIT) { | |
if (us_rising_4isr_en) { // isr must have triggered | |
pinMode(IN1PIN, OUTPUT); | |
digitalWrite(IN1PIN, LOW); | |
cur_rise_us = us_rising_4isr_en-us_rising_4isr_st; | |
slow_rise_us += (cur_rise_us-slow_rise_us)/12; | |
sp("Rise_US:"); spt(cur_rise_us); | |
sp("SlovAvg_/12:"); spl(slow_rise_us); | |
last_read = millis(); | |
#warning "We're resetting last_read millis at the END of the rise, so our state timing is variable and dependent on the capacitance and whatnot" | |
state = ST_START; | |
} | |
} | |
return; | |
#endif // TEST_TIMING_ISR | |
/* #define TEST_TIMING_PULLUP */ | |
#ifdef TEST_TIMING_PULLUP | |
if (cmillis-last_read > 50) { | |
for (int cyc=0; cyc<CYCLES; cyc++) { | |
pinMode(IN1PIN, OUTPUT); | |
digitalWrite(IN1PIN, LOW); | |
delayMicroseconds(12); | |
cmicros = micros(); | |
pinMode(IN1PIN, INPUT); | |
read_cycle(); // fills global unsigned long buf_v[BS] | |
endmicros = micros(); | |
pinMode(IN1PIN, OUTPUT); | |
digitalWrite(IN1PIN, LOW); | |
smooth_buf(buf_v_smooth, buf_v, BS, 15, true); | |
/* for (int i=0; i<BS; i++) // assign estimated timings */ | |
/* buf_us[i] = (unsigned long)((i*((double)(endmicros-cmicros)))/BS); */ | |
/* for (int i=0; i<BS; i+=15) { */ | |
/* /1* sp("us:"); spt(buf_us[i]); *1/ */ | |
/* sp("v:"); spt(buf_v[i]); */ | |
/* sp("sv:"); spl(buf_v_smooth[i]); */ | |
/* } */ | |
/* delay(2); */ | |
/* return; */ | |
if (!cyc) | |
copy_cycle(buf_mavg_div, buf_v_smooth, BS); | |
else | |
merge_cycle_mavg(buf_mavg_div, buf_v_smooth, 4, BS); // merge difference | |
} | |
for (int i=0; i<BS; i++) // assign estimated timings | |
buf_us[i] = (unsigned long)((i*((double)(endmicros-cmicros)))/BS); | |
get_minmax(&curmin, &curmax, buf_mavg_div, BS); | |
int count; | |
count = find_cnt_at_thresh(buf_mavg_div, BS, TRIGGER_FRAC, curmin, curmax); | |
float slowcount=count; | |
slowcount += (count-slowcount)/3; | |
#ifdef OPT_PLOT_DATA | |
for (int i=0; i<CHOP; i+=2) { | |
/* sp("us:"); spt(buf_us[i]); */ | |
sp("v:"); spt(buf_v[i]); | |
sp("smv:"); spt(buf_mavg_div[i]); | |
sp("scnt:"); spt(slowcount); | |
/* sp("min:"); spt(curmin); */ | |
/* sp("max:"); spt(curmax); */ | |
/* if (i>=count) { */ | |
/* sp("trig:"); */ | |
/* spt(curmin + (curmax-curmin)*TRIGGER_FRAC); */ | |
/* } else { */ | |
/* sp("trig:"); */ | |
/* spt(curmin); */ | |
/* } */ | |
/* sp("count*100:"); */ | |
/* spt(count*100); */ | |
spl(""); | |
} | |
#elif defined(OPT_PLOT_COUNT) | |
sp("c:"); spt(count); | |
sp("smc:"); sp(slowcount); | |
spl(""); | |
#endif | |
/* lastv = buf_v[BS-1]; */ | |
/* for (int i=0; i<3; i++) { */ | |
/* lastv += analogRead(IN1PIN); */ | |
/* delayMicroseconds(1000); */ | |
/* } */ | |
pinMode(IN1PIN, OUTPUT); | |
digitalWrite(IN1PIN, LOW); | |
last_read = millis(); | |
return; | |
smoothv = (float)smoothv + (float)(lastv-smoothv)/3; | |
slowv = (float)slowv + (float)(lastv-slowv)/8; | |
basev = (float)basev + (float)(lastv-basev)/98; | |
last_read = cmillis; | |
if (cmillis-last_print > 20) { | |
/* sp("us:"); spt(cmillis-last_print); */ | |
sp("v:"); spl(lastv-basev); | |
/* sp("smv:"); spt(smoothv-basev); */ | |
/* sp("slv:"); spl(slowv-basev); */ | |
/* spl("Hit 0"); */ | |
/* pinMode(IN1PIN, OUTPUT); */ | |
/* digitalWrite(IN1PIN, LOW); */ | |
last_print = cmillis; | |
} | |
} | |
return; | |
#endif | |
/* #define TEST_TIMING_OPCS */ | |
#ifdef TEST_TIMING_OPCS | |
Serial.print(opcs.readCapacitivePin(IN1PIN)); | |
delay(250); | |
#endif | |
/* #define TEST_TIMING */ | |
#ifdef TEST_TIMING | |
pinMode(IN1PIN, INPUT_PULLUP); | |
delayMicroseconds(10000); | |
pinMode(IN1PIN, INPUT); | |
int i; | |
int v; | |
i=0; | |
while ((v = analogRead(IN1PIN))) { | |
i++; | |
spt(i); | |
spl(v); | |
} | |
spl("Hit 0"); | |
/* pinMode(IN1PIN, OUTPUT); */ | |
/* digitalWrite(IN1PIN, LOW); */ | |
delay(250); | |
return; | |
#endif | |
switch (state) { | |
case ST_START: | |
digitalWrite(IN1PIN, HIGH); | |
hightime_us = cmicros; | |
state = ST_HIGHWAIT; | |
break; | |
case ST_HIGHWAIT: | |
if (cmicros - hightime_us >= highwait_delay_us) { | |
digitalWrite(IN1PIN, LOW); | |
pinMode(IN1PIN, INPUT); | |
int val = digitalRead(IN1PIN); | |
lowtime_us = micros(); | |
procval(1, val); | |
state = ST_PROCESSING; | |
} | |
break; | |
case ST_PROCESSING: | |
state = ST_LOWWAIT; | |
lowtime_us = micros(); | |
break; | |
case ST_LOWWAIT: | |
if (cmicros - lowtime_us >= lowwait_delay_us) { | |
state = ST_START; | |
} | |
break; | |
} | |
} | |
void procval(int sensor, int val) { | |
if (!val) in1zeros++; | |
else in1ones++; | |
in1ctr++; | |
// Check for threshold and reset counters if needed | |
if (in1zeros >= MAX_RATIO_CTR || in1ones >= MAX_RATIO_CTR || in1ctr >= MAX_RATIO_CTR) { | |
in1zeros -= RATIO_CTR_RESET_VAL; | |
in1ones -= RATIO_CTR_RESET_VAL; | |
in1ctr -= RATIO_CTR_RESET_VAL; | |
} | |
// Prevent counters from going negative | |
in1zeros = __max(in1zeros, 0); | |
in1ones = __max(in1ones, 0); | |
in1ctr = __max(in1ctr, 0); | |
// Adjust highwait_delay_us based on the ratio, but only if we have enough samples | |
if (in1ctr > MIN_RATIO_CTR) { | |
float ratio = (float)in1ones / (in1zeros + in1ones); | |
// Protect against divide by zero | |
if (in1zeros + in1ones > 0) { | |
// Adjust delay based on observed ratio | |
if (ratio < 0.5) { | |
if (highwait_delay_us > 9) | |
highwait_delay_us -= 1000; // Increase delay if more zeros | |
} else if (ratio > 0.5) { | |
highwait_delay_us += 1000; // Decrease delay if more ones | |
} | |
// Print ratio and current high wait delay | |
sp("CTR:"); spt(in1ctr); | |
sp("HLRatio:"); spt(ratio); | |
sp("HighWait_us:"); spt(highwait_delay_us); | |
sp("1Zeros:"); spt(in1zeros); | |
sp("1Ones:"); spl(in1ones); | |
} | |
} | |
// Reset for the next cycle if needed | |
if (in1ctr >= MAX_RATIO_CTR) { | |
in1zeros = 0; | |
in1ones = 0; | |
in1ctr = 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment