-
• #27
1) functions in arrays addressable by index and the index is the bit that has changed.
2) indeed - because memory is in very short supply in MCs.
3) Polling or interrupt driven, both methods will provided the bits that are pressed, and the application has to figure what happened by comparing to the previously received bits. Yes, you would make one of the capacitive pads to turn on the C# - when the bits switches to 1 - and the other turn if of C#. The functions would turn on the signal generator that is tuned to a C#. The question is how many signal (generator) you have... I assume you have one for every key.I'm not an audio engineer, but from the past I know you can use various devices to turn on the path and let the signal pass to a sum... I'm thinking about a 4916 or better a 4066 - see for example https://www.ti.com/lit/ds/symlink/cd74hc4016.pdf?ts=1632556242364 , or a DG408LE https://www.vishay.com/docs/78084/dg408le.pdf. You could also use an op-amp.
For producing a sound based on a rotary device: yes you can use a pot and poll with an ADC... (I did this with a circuit to excite an (RF) LC circuit to figure its resonance frequencies... see Espruino controlling LC resonance experiment in HAM Radio class). But you also can just use an encoder that delivers you pulses and a direction - clock and counter clock wise. You set a watch - like you do on an interrupt of a device - and then read the direction in the function and adjust the frequency... In my ham circuit I used watch and polling: watched buttons that increase / lower the frequency, and polling of a sliding pot... The pot I use two ways: controlling the frequency increment/decrement speed when clicking (and holding) the red and green buttons, and when pressed at the same time, the app switches into the analog mode where I poll pot and map it to a frequency.
-
• #28
@allObjects you are a Godesend! I'm going to stop asking questions for a while and go try to build build build with all of this magnificent teaching. You (and others on this thread) are really very good educators and I respect the levels of complexity.
Would love to see some pictures (when the mood strikes you) of things you've made - not that I've ever ventured into ham, but it is intriguing. The closest I've come is an SDR dongle as a listener/explorer of frequencies...
-
• #29
...did dig up the code:
` // resonance.js // resonance experiment // PWM signal generator with variable frequency // agitates LC with variable L and C // observed to find resoncances // *** Exciter, resonance, detector circuit // // <--sigPin -----------------------------------------. // resonance TP3 TP4 TP5 | // signal s multi-tap / / D / | // detected MW(AM) HF Air .----.-----|>|-----.-----: // 0..290mV Coil 100 wind | | Ge E110LF | | // 0.5mm Cu on ( | Si 1N4148 | | // 41mm/1-5/8" ( | | | // _ ( | |+ | // TP1 | |_| 3.3VPP TP2 ( === === / // / / ( | Cx C1 --- \ // >.-outPin--/\/\/\-->. ( | 1nF 3.3u |- / // PWM R1 330R ) ( | 2.2n* | R2 \ // variable ) 8x ( | 3.2n | 56K / // frequency f 20 ) 10 ( | 4.7n | | // 32K..262KHz... w | w | | 5.7n | | // for receiver | | | 9.4n | | // antenna ___ ___ ___ ___ ___ // simulation - - - - - // . . . . . // *** UI / Control / Feedback circuitry to vary frequency f // // <-slidePin-------------------------------------------------. // 0..3.3V | // 3.3V 3.3V 3.3V 3.3V 3.3V | // ^ ^ ^ ^ ^ | // | | | extra | | | // | | | read of | | | // \ f down | f up | signal | / | // R3 / button | button | button | \ | // 100..300R \ red | green | yellow | R4 / R5 | // / SW1 | SW2 | SW3 | 10K \<-/\/\/\-: // | | | | Sli / 1K | // .-./| | | | der \ | // speaker | | | | | | / | // '-'\| | | | \ | // _ +| | | | | | // | |_| === | | | | | // 3.3VPP --- C2 | | | | |+ // PWM -| 33u | | | | === // >-audioPin-->' | | | | C3 --- // wave 7 octaves lower | | | | 3.3u |- // | | | | | // <-btnPins[1]-----------' | | | | // <-btnPins[0]---------------------' | ___ ___ // <-sRBtnPin---------------------------------' - - // internal input_pulldown (30k..40K) . . var go = true // keep going on self invocation / 'interval' , f = -1 // frequency put out , fMin = 32768 // lowest used freequency , fMax = 262144 // highst, 65536 * 4 = 18 bits unsigned () , fUp = true // frequency hopping up (wobble) , outPin = B7 // wave exciting antenna coil , duty = 0.5 // 0..1 , audioPin = B10 // speaker , audioDiv = 128 // 7 octavae lower into audible space , s = 0.0 // signal (resonance, rectified) - discrete , smV = 0.0 // signal in [mV], continuously , sigPin = A0 // signal pin (analog read 0..1=0..3.3V) , sChngSens = 0.7 // signal change sensitivity relative , sCalib = 303.334 // signal calibration to show mV , sRBtnPin = B13 // singal extra read button pin , ctrlMode = 0 // 0=buttons, 1=slider , ctrlModeI = 1000 // [ms] delay after ctrlMode chnage , btnPins = [B15,B14] // down|up button, both = ctrlMode change , debounced = false // on button press do custom debounce , debounceI = 10 // [ms] debounce interval , btnHopInt = 10 // [ms] button hop interval min, slider modified , btnHopMul = 49 // button hop interval extension multiplier , slidePin = B1 // analog to read voltage divider from slider , slideVal = 0.0 // last read different slider value , slideSens = 0.003 // slider sensitivity to detect different val , slideAdjM = -0.005 // adjust slider min to cover [0..1] , slideAdjR = 1.05 // adjust slider range to cover [0..1] , slideHopI = 50 // [ms] slider check/hop interval ; var hTFact = 1.059463094359; // half tone factor (2**1/12)**12 = 2 oktav var qTFact = 1.029302236643495; // quarter tone (2**1/24)**24 = 2 oktav var eTFact = 1.014545334937521; // eight tone f (2**1/48)**48 = 2 oktav var lon = true; // log on function log() { console.log.apply(console,arguments); } pinMode(outPin,"output"); pinMode(sigPin,"analog"); pinMode(sRBtnPin,"input_pulldown"); btnPins.forEach(function(b){ pinMode(b,"input_pulldown"); }); pinMode(slidePin,"analog"); var cnt = 0; function upAndDown(fInp,fCdPre) { var fChgd = (fInp != f); // f changed if (fChgd) { // ----- put freq out if changed f = fInp; fChngedT = getTime(); analogWrite(outPin,duty,{freq:f}); analogWrite(audioPin,duty,{freq:f/audioDiv}); } var btnVals = digitalRead(btnPins), nextF = f, hopI, val; // if (lon) log(++cnt, btnVals, ctrlMode); if (btnVals && ( ! debounced)) { debounced = true; setTimeout(upAndDown,debounceI,fInp,fChgd); // ...w/ same f } else { debounced = false; if (btnVals === 3) { // --- ctrlMode change ctrlMode = (ctrlMode === 0) ? 1 : 0; if (lon) log("New ctrlMode =",ctrlMode); setTimeout(upAndDown,ctrlModeI,fInp,false); // ...w/ same f } else { if (ctrlMode === 0) { // --- calc next freq on button presses slideVal = -1.0; // forces slide value on ctrlMode change if (btnVals === 1) { // --- up nextF = (f<fMax) ? f*tFact : fMax; } else if (btnVals === 2) { // --- down nextF = (f>fMin) ? f/tFact : fMin; } hopI = Math.floor((1+analogRead(slidePin)*btnHopMul)*btnHopInt); } else { // --- calc next freq from slider pos / voltage divider val = (slideAdjM + analogRead(slidePin)) * slideAdjR; if (Math.abs(val - slideVal) > slideSens) { slideVal = val; nextF = Math.floor(fMin + (fMax - fMin) * slideVal); nextF = (nextF<fMin) ? fMin : (nextF>fMax) ? fMax : nextF; } hopI = slideHopI; } val = analogRead(sigPin); if (lon) { if ( digitalRead(sRBtnPin) || (fChgd || fCdPre) || ( (Math.abs(s - val) > s*sChngSens) && (f !== fMin) && (f !== fMax) ) ) { s = val; smV = val * sCalib; log(++cnt,"f =",Math.round(f),Math.floor(smV)); } } if (go) setTimeout(upAndDown,hopI,nextF,false); } } } function h() { go = false; } // halt function onInit() { go = true; tFact = eTFact; ctrlMode = 0; fUp=true; upAndDown(fMin); } setTimeout(onInit,500); // while dev'ing `
-
• #30
Sat 2021.09.25
I wanted to use an ADC (potentiometer) to alter PWM in realtime while the note is playing continuously, could I?
Code snippets:
http://www.espruino.com/Software+PWM
http://www.espruino.com/ADC
found at
http://www.espruino.com/Tutorials
Keeping it short so you can:post #28 'go try to build build build . . . '
-
• #31
@allObjects , all useful stuff above Ta,. ...not to distract from @bertjerred 's build mission but Think it may be useful here to note what happens if a setWatch interrupt occurs while the previous SetWatch function is still running. Was going to test but see you comented on the subject ( 7 yrs ago :). http://forum.espruino.com/comments/11932979/
Is it correct still that setWatch triggered functions are queued and executed by the JS Interpreter in the order in which they occurred?
(* in the simple case assuming the advanced IRQ' parameter features of set watch are not used ) -
• #32
Sun 2021.09.26
http://www.espruino.com/Reference#l__global_setWatch
'Internally, an interrupt writes the time of the pin's state change into a queue with the exact time that it happened, and the function supplied to setWatch is executed only from the main message loop. However, if the callback is a native function void (bool state) then you can add irq:true to options, which will cause the function to be called from within the IRQ. When doing this, interrupts will happen on both edges and there will be no debouncing.'
-
• #33
any event / interrupt goes into the queue w/ related context and queue is worked on by the JS 'loop'. Espruino error flags tell you about overruns - http://www.espruino.com/Reference#l_E_getErrorFlags .
Messing with Espruino's architectural setup is not forbidden, but one should not be surprised when things don't go the way as expected... For 'extremely' time sensitive things, write a dedicated client ad let it co-op with Espruiono as the 'overseer'.
-
• #34
I've never seen anyone draw their circuit in the comments before and I love it so much.
-
• #35
Thank you :)
-
• #36
Mon 2021.09.27
reply to post #34
'I've never seen anyone draw their circuit in the comments before and I love it so much'
@bertjerred feel free to peruse the plethora of posts that @allObjects has done over the years. His expertise in complete embedded ASCII circuit documentation is legendary here within the Espruino forum. I too love it when his skill with a new presentation piques a forgotten hidden corner of the cob web infested aging mind. Always something to garner from that insight.
I also am old school and keeping documentation within the code file ensures that it won't get lost or more so not forgotten during a future code update or modification. When I stumble across some of those special treats, I'll send or post here.
reply to post #35post #28 Thank you :) Keeping it short so you can: 'go try to build build build . . . '
Hope you had an enjoyable time over the week end bit twiddling!! ;-)
-
• #37
@Gordon: does the MPR121 module support the filtered and baseline touch values like the Adafruit MPR121 library? The functions are: filteredData(pin) and baselineData(pin).
https://github.com/adafruit/node_mpr121/blob/master/index.js
-
• #38
Hi! It doesn't support it, but it should be easy enough to add. If you stick this code after you instantiate it, it should add the functionality:
mpr.readWord = function(reg) { i2c.writeTo({address:0x5A, stop:false}, reg); var data = i2c.readFrom(0x5A, 2); return (data[1] << 8) || data[0]; }; mpr.readByte = function(reg) { i2c.writeTo({address:0x5A, stop:false}, reg); return i2c.readFrom(0x5A, 1)[0]; }; mpr.filteredData = function(pin) { if (pin<0 || pin>=12) throw new Error("Invalid pin"); return this.readWord(0x04 + pin*2); }; mpr.baselineData = function(pin) { if (pin<0 || pin>=12) throw new Error("Invalid pin"); return this.readByte(0x1E + pin)<<2; };
If you can let me know if it works for you then I'll add it into the main module - but I can't test it here as I don't have the hardware :)
-
• #39
Thanks, @Gordon.
I am getting a response from the following code using your code snippet.
I am just logging out the ouput of each MPR121 sensor pin as I run my finger over them.
Two observations: 1) the filteredData output seem to indicate which pins were touched as 2^pin. If pin 0 and pin 2 are touched, I get 2^0+2^2 = 5. I was expecting to get a value indicating the capacitance. 2) the baselineData output is 4 x the filteredData output, but not consistently.// set up I2C var i2c = new I2C(); i2c.setup({ scl : D28, sda: D29 }); // Setup pins and interface to MPR121 const sclPin = D28; const sdaPin = D29; const irqPin = D30; // interrupt from MRP121 //I2C1.setup({scl:sclPin,sda:sdaPin}); // IRQ is Active Low, open Drain (ref datasheet) - make use of internal pullup pinMode(irqPin,'input_pullup'); // connect to MRP121 via Espruino MRP121 library function mprConnected() {console.log ("MPR121 Connected");} var mpr = require("MPR121").connect(i2c, mprConnected , { address: 0x5A}); // define function for interrupt (fires on touch AND release) function readValues() { for(let i=0; i<12;i++) console.log(mpr.filteredData(i) + ", " + mpr.baselineData(i)); } mpr.readWord = function(reg) { this.write(reg); var data = this.read(2); return (data[1] << 8) || data[0]; }; mpr.readByte = function(reg) { this.write(reg); return this.read(1)[0]; }; mpr.filteredData = function(pin) { if (pin<0 || pin>=12) throw new Error("Invalid pin"); return this.readWord(0x04 + pin*2); }; mpr.baselineData = function(pin) { if (pin<0 || pin>=12) throw new Error("Invalid pin"); return this.readByte(0x1E + pin)<<2; }; setInterval(function(){ readValues(); console.log(" " +"\n"); }, 5000);
2 Attachments
-
• #40
Ok, thanks! I think what might be happening is the readWord/readByte isn't working properly - they're just effectively reading from address 0. It could be because the sensor resets if it receives a STOP I2C signal between packets...
Please could you try:
mpr.readWord = function(reg) { i2c.writeTo({address:0x5A, stop:false}, reg); var data = i2c.readFrom(0x5A, 2); return (data[1] << 8) || data[0]; }; mpr.readByte = function(reg) { i2c.writeTo({address:0x5A, stop:false}, reg); return i2c.readFrom(0x5A, 1)[0]; };
-
• #42
Great, glad it's working! Let me know when you're happy that it's working as expected and I can push an update to the module.
-
• #43
@Gordon, skimming over the code in post #38 as the extension (and post #39 in its use), I think that the 4th 'item' in post #39 should read
.baselineData
as said in post #39 by @mix2009 (instead of repeating 3th 'item'.filteredData
). ...just in case for what you will copy into the update of the module... ;-) -
• #46
Great! I'll update the module
Wow, thank you. So many things here for me to digest. As always, please forgive my lack of experience, but 1) these are functions within a function... each assigned to 1-bit changes? Is that right? and 2) Espruino (and JavaScript) interpret 'on the fly,' so to speak?
And 3) if I was using, say, a polling version for MPR121 key strokes successfully (e.g., I can play a C# for as long as I hold down the appropriate key), and I wanted to use an ADC (potentiometer) to alter PWM in realtime while the note is playing continuously, could I?
I'm not sure I fully understand the order in which the code gets interpreted by the Espruino, and therefore where to embed my "mod wheel" potentiometer loop. Or maybe I just get confused by loops within loops...
In any case, I sure do appreciate all of this, @allObjects!