→ Our classmate Nasif was incredibly helpful in the testing phase, and we ended up verifying it works, the structure and subsequently ordering the needed quantities. Velostat purchased from
The original Processing Code for a square grid is as follows below. We figured out we don’t necessarily need to use Processing as that would mean learning Java.
‣
Java Code for a squared coppered tape grid
Through the p5 prototyping below, we realised we need around 90 sections (30 below divided by 3, creating 90).
Problem: getting enough pins to connect around 90 sections
Can we use the I2C bus and an I2C multiplexer for analog signal from copper + velostat? Would the output of I2C communication be helpful in retrieving the range output from the pressure applied on the Velostat?
It seems like the Arduino Nano only takes 3 multiplexers (3 * 5 wires needed and a max of 19 Arduino pins). For the Sparkfun multiplexer
, that would mean 3*16 = 48 regions of sensing, but it’s looking like we need something closer to 90. It’s possible I’m being shortseighted. Would using an array mean we need more connections or less? More actually.
Solution is basically between using an Arduino Mega with 54 pins (16 with PWM) and the Spark Fun or using more I2C multiplexers? [ Question for Prof ]
We’re building a capacitive sensor. // custom pressure sensor.
When Serial is open, it can’t be open to p5 and TouchDesigner at the same time. There however can be two way serial. There’s also other ways to communicate besides serial - like Midi or OSC.
Main issue is that 90 is a lot of sections and a lot of code.
Actually we can use 2 sections ⭕️, making it 32 total or fewer sections in the palm and more in the fingertips = 15 + 30.
Need to verify this information but the Arduino Mega has no wifi.
I can chain together the Analog/Digital MUX, which means that the amount of Nano pins wouldn’t be a constraint.
Sparkfun Analog to digital MUX Multiplexer has a different function than the I2C multiplexer. An I2C multiplexer is for connecting I2C devices that have the same address, so that way you don’t need to change the addresses, you can target the multiplexer instead.
Next steps
Playtest where we have a few sections of the circular sensor working. Visuals can be crappy.
Operating Conditions: Supply Voltage Range, Vcc → HC Types 2V to 6V
→ When do we know if we’ve reached the maximum?
🍎 This may be a problem… “Only one of these is selected at any time, based on the control pins.”
How much resistance do resistors need?
To determine the resistance value for your resistor, we need to analyze the circuit and the voltage behavior of the CD74HC4067 in conjunction with the Arduino Nano IoT 33.
Voltage Output on the Channel Pins
The output voltage from any channel pin (e.g., C0) of the CD74HC4067 will depend on the input voltage supplied to the COM (SIG) pin and the control logic state. The multiplexer simply connects the selected channel (C0–C15) to the COM pin with minimal resistance.
Supply Voltage (VCC):
The CD74HC4067 works with a power supply voltage of 2–6V. If you're powering the board from the Arduino's 3.3V pin, the output voltage on any channel pin will be approximately 3.3V when HIGH.
Output Impedance:
The ON resistance of the multiplexer (when a channel is active) is around 70–100Ω (depending on VCC). This slight resistance doesn't significantly affect most circuits.
Resistor Calculation for Your Homemade Sensor
For your sensor connected to C0, the resistor to ground is likely acting as a pull-down resistor or forming a voltage divider to measure an analog signal. The resistance value depends on:
Desired Voltage Range Across the Sensor:
The voltage across the sensor should fall within the Arduino Nano IoT 33's ADC input range (0–3.3V). The ADC has a 12-bit resolution, so you'll get finer measurements if the voltage variation across the sensor spans as much of this range as possible.
Sensor Characteristics:
What is the expected resistance range of your homemade sensor? This is critical for determining the pull-down resistor.
Case 1: Sensor as a Variable Resistor
Case 2: Fixed Resistor for Digital Input
If your sensor output is binary (e.g., HIGH/LOW detection), use a pull-down resistor:
Typical values: 10kΩ works well in most cases.
💡
C0 → Sensor → GND (through pull-down resistor).
We have 27 pins left! 🍓
Writing code to read sensors
‣
Small Mat Experiment code by Nasif Rincon
‣
Code by Instructables for when all pins are connected to sensors plus using Arduino Mini Pro 5V
Start project with Arduino MKR WiFi 1010 as a replacement for Nano 33 IoT
‣
Prompt for correct pins
Thanks, that's great. Both multiplexers have the same function, where their I/O Channel pins are all connected to copper. A sandwich of copper, velostat and copper is what gives a reading.
Ideally, i'd have a 3 by 18 matrix with 54 sensors.
So should the 2 multiplexers be treated as input vs output, since they both serve the same function?
Also, my matrix currently has 3 rows and 18 columns. The rows should read a range analog signal.
The 3 rows are connected to pins C0, C1 and C2 to the multiplexer whose SIG pin is connected to Arduino's A0 pin. For that multiplexer, S3 pin is connected to A1 arduino pin, S2 is connected to A2, S1 to A3 and S0 to A4.
The first 9 columns are connected to pins C8, C7, C6, C5, C4, C3, C2, C1 and C0 on the multiplexer whose SIG pin is connected to Arduino's D08 pin. For that multiplexer, S3 pin is connected to D09 Arduino pin, S2 to D10, S1 to D11 and S0 to D12.
The second set of 9 colums are connected to pins C11, C10, C9, C8, C7, C6, C5, C4 and C3 on the multiplexer whose SIG pin is connected to Arduino's A0 pin. Again for this multiplexer, S3 pin is connected to A1 arduino pin, S2 is connected to A2, S1 to A3 and S0 to A4.
I want you to help me modify my code so that the connections are all correct and even help me read the value of the rows and colums in the order I outlined.
Prompt #2:
I followed your instructions, the monitor is printing As!
The next step is making these readings useful.
I want to read values for mux1, channels C0, C1 and C2. Meaning - the rows, just as an example + print on the monitor the readings from those pins.
How can I set that up?
Let me know also about next steps, if there's any calibration that still needs to be done or something else.
The goal is to eventually map those readings to output a value from 0 to 20 for each of the 54 sensors. The idea is to receive the data in a different React application and when the copper columns + velostat + copper rows get pressure, the number seen on the screen for each of the 54 quadrants would increase accordingly.
‣
Code for getting baseline readings
💡
First 9 columns (C8 down to C0) from the second multiplexer (D8)
Next 9 columns (C11 down to C3) from the first multiplexer (A0)
What we did:
finished hooking up sensors
Figured out the circuit
Chained multiplexers
Added p5 to a React application, started setting up the structure to receive and handle API data for top stories
Added a way to real serial monitor on VS Code
Wrote the code for chaining and reading 54 sensors
Wrote the code to do first baseline readingsz
Feedback from Playtesting #1:
News showing is it bad? Or chaotic? Could show comments instead of news.
Youtube Trending Videos
Visuals:
can use text for news - text rotating circles like in news channels
Will it be hard to get
Can use felt and
What if we visualize each news story as its own organism?
Play act or
Next week: bring an actual visuals!
Next steps - AI reference advice
‣
Code
Next steps would be:
Calibration Phase:
Take readings when no pressure is applied (baseline)
Take readings with light pressure
Take readings with maximum expected pressure
This helps establish your min/max range for mapping
Data Processing:
Serial Communication Format:
For React integration, you'll want a consistent data format. Here's one approach:
React Integration:
Your React app would need to:
Connect to the serial port (using a library like serialport)
Parse the JSON data
Update your UI based on the values
Would you like me to provide a more detailed implementation of any of these steps? Also, do you want to start by testing the row readings first to see what kind of values you're getting?
‣
Testing prompt 2x3 rows
Code for 2x3 matrix
Monitor readings from code above after resetting arduino
‣
Question about pinMode
For the Arduino Nano IoT 33, what is the function that allows us to analog read the A0 pin?
Does function pinMode also work to set analog pins as INPUT or OUTPUT or is it only for digital pins?
Project building: prep for playtest 2
@December 2, 2024
Tasks aren’t building with the same code.
If I reset → unplug → build - monitor doesn’t complete successfully ❌
If I reset without unplugging, monitor reads ✅
But current readings aren’t running.
React can’t read if platformIO is reading.
Print out pics of what’s happening → For interaction.
multiplexee does the same as the for loop
variable resistor = we have an homemade w velostat
there’s a multiplexer library??!
Assumptions: do people understand the touch grass thing? Does the concept make sense.
Make sure to handle cleanup properly in your React component (closing the serial port when the component unmounts)
Debug
I can list all the ports + select port from the terminal.
ls /dev/cu.*
Only one thing can be connected at a time. It’s not broadcasted.
Don’t get rid of if serial.
I need I close serial monitor,
Have p5 send a 0, so that it knows when it’s been pinged. Same as I was sending over
Mouse press could be the condition and then it reads automatically. Make it user initiated. Don’t do a while loop.
As you work on this any microcontroller-to-computer application, you will be switching back and forth between the app that programs the microcontroller (in this case, the Arduino IDE) and the app that the microcontroller is communicating with (in this case, p5.js in the browser). You have to keep in mind that only one of these at a time can access a serial port.
That means that when you want to reprogram your Arduino from the Arduino IDE, you should to stop your sketch in the browser window to do so. Then, restart the browser sketch when you’re done reprogramming the Arduino. You don’t need to quit the Arduino IDE each time, because it knows to release the serial port when it’s not programming. However, you do need to close the Serial Monitor in the Arduino IDE when you are using WebSerial in the browser.
We changed the Arduino Code to continuously read, but C1 is still off the charts. Is it because electricity takes the path of least resistance?
Code for Ripples on p5 editor - check if flower or ripple output without p5.WebSerial library
Possibility to improve cleanup on React code - not implemented.
For these 3 error messages:
The RAF (RequestAnimationFrame) violation - this is just a performance warning and not related to our serial connection
The message port error - this is a Chrome-specific message that's actually not an error, just a notification
The Bluetooth service message - this is just informing us that AirPods were filtered out of the serial port selection
Playtest #2 Feedback:
Don’t use real news
Overwhelming cloud of negative words
AI generated Gibberish
If you put the installation on the floor people will step on it.
❤️ Grounding ❤️
When you come in, i should
Claspy mechanisms / things
Nasif likes when name contextualizes
Rubbery gas kit - for rubber
Talk to phil
2 clasps against each other
L brackets
Digital noise that dissipates with collective play
Demo present or video/pics to submit
Have people interact
Class can go to staging space
ICM Visual Questions:
Effect of text melting / dissipating into a swirl → into a pond
setup function in p5 is being fired repeatedly
Looked into GitHub issues → could be because of States
I’m also putting the variables in a cleanup function.
using the updateWithProps p5 react wrapper function.
Connecting all 36 columns x 3 rows = 108 home made FSRs
‣
Explanation of circle shape and connections between multiplexers
Now we'll be adding more sensors. Especifically we now have a table of 3 rows and 36 columns for the pressure sensor matrix, instead of 3 times 9. I'm now using 3 multiplexers.
it's important that the 108 values are output in the right order.
The rows are still reading:
A0 - top row
A1 - middle row
A2 - bottom row
The 36 columns are connected through multiplexers.
This is how the multiplexers are connected to the Arduino
First multiplexer - let's call it MTL for Multiplexer Top Left:
SIG pin of the multiplexer- D8 pin of the arduino
S3 - D9
S2 - D10
S1 - D11
S0 - D12
Pins C15 to C7 are connected to the first 9 columns, where first column is connected to C15, second column to pin C14 and so on.
Pins C3 to C0 are connected to columns 10, 11, 12 and 13.
Second multiplexer - let's call it MBL for Multiplexer Bottom Left:
SIG - A3 pin of the arduino
S3 - A4
S2 - A5
S1 - A6
S0 - A7
On this second multiplexer MBL, pins C15 to C11 are connected to column 14 to column 18. Pins C10 to C2 are connected to column 19 to 27
Third multiplexer - let's call it MTR for Multiplexer Top Right:
SIG - D7 pin of the arduino
S3 - D6
S2 - D5
S1 - D4
S0 - D3
On multiplexer MTR, column 28 to 36 are connected to pins C15 to C7.
Can you write code that will output all combinations of 3 rows and 36 columns in the right order from column 1 to 36, measuring row 1 and col 1 first, then row 2, col 1, then row 3, col 1 and at last row 1, col 36, then row 2, col 36 and last value would be row 3, col 36?
Leave comments so I understand what your decisions have been
Fabrication
For the Optoma projector to work, we’d need to raise the structure by 15 inches~
Touch Grass is an interactive digital pond. Its shape ensures everyone has equal stakes and engaging in accessible collective play, which creates a state of mind for open conversation. It conveys the power of collaboration in society, as you need everyone to touch it as the same time to see the full ripple effects. With more iterations, it could be an interface for brainstorm and shared physical computing. Currently it's very large, unstable and difficult to bring to where it can have an impact. The grant is to manufacture of a custom round 32'' PCB LED display that can replace the bin, projector, metal stand and counterweight setup. Afterwards, I'll explore more direct ways to spark conversation at a time where we need communities to hold and explore their differences.
Folk Computer due to how it can bring elders and children alike together to brainstorm together and solve societal issues without the downside of addiction that's often caused by more portable forms of tech, the relationship between art materials, supply chain and tariffs, as well as Cyber Feminism.
More hackathons solving specific issues shared by studios, policy makers or communities with very real constraints. Vibe Shift was an event where I saw people from all parts of creative tech, which was very positive. But as the world shifts with AI, learning outside of a typical classroom through events, hackathons and crash courses followed by working on your own projects solving very specific issues that exist outside of a bubble is the way forward - as opposed to studying, memorizing and solving self-selected arbitrary problems, where you choose your constraints.
/*
This example reads audio data from an Invensense's ICS43432 I2S microphone
breakout board, and prints out the samples to the Serial console. The
Serial Plotter built into the Arduino IDE can be used to plot the audio
data (Tools -> Serial Plotter)
Circuit:
* Arduino/Genuino Zero, MKR family and Nano 33 IoT
* ICS43432:
* GND connected GND DONE
* 3.3V connected to 3.3V (Zero, Nano) or VCC (MKR) DONE
* (LRCLK or) WS connected to pin 0 (Zero) or 3 (MKR) or A2 (Nano)
* CLK connected to pin 1 (Zero) or 2 (MKR) or A3 (Nano)
* SD connected to pin 9 (Zero) or A6 (MKR) or 4 (Nano)
created 17 November 2016
by Sandeep Mistry
*/
#include <I2S.h>
void setup() {
// Open serial communications and wait for port to open:
// A baud rate of 115200 is used instead of 9600 for a faster data rate
// on non-native USB ports
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
// start I2S at 8 kHz with 32-bits per sample
if (!I2S.begin(I2S_PHILIPS_MODE, 8000, 32)) {
Serial.println("Failed to initialize I2S!");
while (1); // do nothing
}
}
void loop() {
// read a sample
int sample = I2S.read();
if (sample) {
// if it's non-zero print value to serial
Serial.println(sample);
}
}
#include <I2S.h>
void setup() {
Serial.begin(115200);
while (!Serial) {
; // Wait for serial port to connect. Needed for native USB only
}
// Start I2S at 8 kHz with 32-bits per sample
if (!I2S.begin(I2S_PHILIPS_MODE, 8000, 32)) {
Serial.println("Failed to initialize I2S!");
while (1); // do nothing
}
}
void loop() {
// Read a sample from the I2S microphone
int sample = I2S.read();
// Only send significant audio levels
if (sample > 100) { // Threshold to filter out background noise
Serial.println(abs(sample) / 100); // Scale down data for p5.js
}
}
// This example code is in the public domain.
import processing.serial.*;
import processing.opengl.*;
int bgcolor; // Background color
int fgcolor; // Fill color
Serial myPort; // The serial port
int[] serialInArray = new int[225]; // Where we'll put what we receive
int[] pastInArray = new int [225];
float[][] colorTarget = new float[3][255];
float[][] currentColor = new float[3][255];
PVector[][] vertices = new PVector[15][15];
float[] verticesTZ = new float[15];
float w = 30;
float ease = 0.75;
int serialCount = 0; // A count of how many bytes we receive
int xpos, ypos; // Starting position of the ball
boolean firstContact = false; // Whether we've heard from the microcontroller
int tiempoant;
int render=0;
int dif=0;
void setup() {
size(960, 600, OPENGL); // Stage size
noStroke(); // No border on the next thing draw
// Print a list of the serial ports, for debugging purposes:
println(Serial.list());
// I know that the first port in the serial list on my mac
// is always my FTDI adaptor, so I open Serial.list()[0].
// On Windows machines, this generally opens COM1.
// Open whatever port is the one you're using.
myPort = new Serial(this, Serial.list()[5], 115200);
for (int j = 0; j < 15; j++) {
for (int i = 0; i < 15; i++) {
vertices[i][j] = new PVector( i*w, j*w, 0);
}
}
}
void draw() {
if (render==1) {
translate(width/4, 100);
rotateX(0.5);
//rotateX(PI/10);
background(0);
for (int j=0; j<14; j++) {
beginShape(QUAD_STRIP);
for (int i=0; i<15; i++) {
stroke(255);
fill(serialInArray[j*15+i], 0, 0);
float x = i*width/15;
float y = j*height/15;
verticesTZ[i] = serialInArray[j*15+i];
vertices[i][j].z += (verticesTZ[i]-vertices[i][j].z)*ease;
vertex( vertices[i][j].x, vertices[i][j].y, vertices[i][j].z);
vertex( vertices[i][j+1].x, vertices[i][j+1].y, vertices[i][j+1].z);
}
endShape(CLOSE);
// println();
}
render=0;
}
}
void serialEvent(Serial myPort) {
// read a byte from the serial port:
int inByte = myPort.read();
// if this is the first byte received, and it's an A,
// clear the serial buffer and note that you've
// h
// ad first contact from the microcontroller.
// Otherwise, add the incoming byte to the array:
if (firstContact == false) {
if (inByte == 'A') {
myPort.clear(); // clear the serial port buffer
firstContact = true; // you've had first contact from the microcontroller
myPort.write('A'); // ask for more
}
} else {
// Add the latest byte from the serial port to array:
serialInArray[serialCount] = inByte;
serialCount++;
// If we have 3 bytes:
if (serialCount > 224 ) {
println(millis()-tiempoant);
tiempoant = millis();
render = 1;
// Send a capital A to request new sensor readings:
myPort.write('A');
// Reset serialCount:
serialCount = 0;
}
}
}
//Pins for the rows and columns in order
int row[] = {12, 11, 10, 9, 8};
int column[] = {A1, A2, A3, A4, A5};
//Array that holds calibration values
int calibration[5][5];
//Variables used to process readings
int minsensor = 254;
int reading = 0;
const int cap = 1000; //cap for the reading values
void setup() {
//Init serial
Serial.begin(112500);
//Set the pin modes
for (int i = 0; i < 5; i++) {
pinMode(row[i], OUTPUT);
pinMode(column[i], INPUT);
}
calibrate();
}
int inByte = 0;
void loop() {
if(Serial.available() > 0){
inByte = Serial.read();
//Serial.print(inByte);
if(inByte == 48){
readMat();
// for(int i=0; i < 5; i++){
// delay(100);
// Serial.write(i);
// }
}
}
//delay(100);
//clearConsole();
}
void calibrate() {
//Initialize the calibration at 0
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
calibration[i][j] = 0;
}
}
//Read each sensor 50 times and get the sum of its readings
for (int k = 0; k < 50; k++) {
for (int i = 0; i < 5; i++) {
digitalWrite(row[i], HIGH);
for (int j = 0; j < 5; j++) {
calibration[i][j] = calibration[i][j] + analogRead(column[j]);
}
digitalWrite(row[i], LOW);
}
}
//Divide the sums by 50 to get the average readings of each sensor
for (int i = 0; i < 5; i ++) {
for (int j = 0; j < 5; j ++) {
calibration[i][j] = calibration[i][j] / 50;
if (calibration[i][j] < minsensor) {
minsensor = calibration[i][j];
}
}
}
// Serial.println();
// Serial.print("Minimum Value: ");
// Serial.println(minsensor);
// Serial.println();
}
void readMat() {
for (int i = 0; i < 5; i++) {
digitalWrite(row[i], HIGH);
for (int j = 0; j < 5; j++) {
//Read the pixels
reading = analogRead(column[j]);
//Limit the reading between its
//standby calibration value and
//our predefined max value
reading = constrain(reading, calibration[i][j], cap);
//Map reading to a byte
reading = map(reading, minsensor, cap, 1, 254);
//Addition upper and lower cap
if (reading < 50) {
reading = 0;
} else if (reading > 254) {
reading = 254;
}
if(reading > 0) {
reading = map(reading, 50, 254, 0, 254);
}
//Format to three digits
// char buffer[4]; // Create a buffer for the formatted string
// sprintf(buffer, "%03d", reading); // Format the number as 3 digits with leading zeros
// //Print the reading
// Serial.print(buffer);
// Serial.print(' ');
//delay(10);
Serial.write(reading);
}
digitalWrite(row[i], LOW);
//Jump to the next row in the Serial
//Serial.println();
}
}
//void clearConsole() {
// for (int i = 0; i < 50; i++) { // Adjust the number of lines as needed
// Serial.println();
// }
//}
//Mux control pins for analog signal (SIG_pin) default for arduino mini pro
const byte s0 = 13;
const byte s1 = 12;
const byte s2 = 11;
const byte s3 = 10;
//Mux control pins for Output signal (OUT_pin) default for arduino mini pro
const byte w0 = 9;
const byte w1 = 8;
const byte w2 = 7;
const byte w3 = 6;
//Mux in "SIG" pin default for arduino mini pro
const byte SIG_pin = 0;
//Mux out "SIG" pin default for arduino mini pro
const byte OUT_pin = 5;
//Row and Column pins default for arduino mini pro
const byte STATUS_pin = 3;
const byte COL_pin = 2;
const boolean muxChannel[16][4]={
{0,0,0,0}, //channel 0
{1,0,0,0}, //channel 1
{0,1,0,0}, //channel 2
{1,1,0,0}, //channel 3
{0,0,1,0}, //channel 4
{1,0,1,0}, //channel 5
{0,1,1,0}, //channel 6
{1,1,1,0}, //channel 7
{0,0,0,1}, //channel 8
{1,0,0,1}, //channel 9
{0,1,0,1}, //channel 10
{1,1,0,1}, //channel 11
{0,0,1,1}, //channel 12
{1,0,1,1}, //channel 13
{0,1,1,1}, //channel 14
{1,1,1,1} //channel 15
};
//incoming serial byte
int inByte = 0;
int valor = 0; //variable for sending bytes to processing
int calibra[15][15]; //Calibration array for the min values of each od the 225 sensors.
int minsensor=254; //Variable for staring the min array
int multiplier = 254;
int pastmatrix[15][15];
void setup(){
pinMode(s0, OUTPUT);
pinMode(s1, OUTPUT);
pinMode(s2, OUTPUT);
pinMode(s3, OUTPUT);
pinMode(w0, OUTPUT);
pinMode(w1, OUTPUT);
pinMode(w2, OUTPUT);
pinMode(w3, OUTPUT);
pinMode(OUT_pin, OUTPUT);
pinMode(STATUS_pin, OUTPUT);
pinMode(COL_pin, OUTPUT);
digitalWrite(s0, LOW);
digitalWrite(s1, LOW);
digitalWrite(s2, LOW);
digitalWrite(s3, LOW);
digitalWrite(w0, LOW);
digitalWrite(w1, LOW);
digitalWrite(w2, LOW);
digitalWrite(w3, LOW);
digitalWrite(OUT_pin, HIGH);
digitalWrite(STATUS_pin, HIGH);
digitalWrite(COL_pin, HIGH);
Serial.begin(115200);
Serial.println("\n\Calibrating...\n");
// Full of 0's of initial matrix
for(byte j = 0; j < 15; j ++){
writeMux(j);
for(byte i = 0; i < 15; i ++)
calibra[j][i] = 0;
}
// Calibration
for(byte k = 0; k < 50; k++){
for(byte j = 0; j < 15; j ++){
writeMux(j);
for(byte i = 0; i < 15; i ++)
calibra[j][i] = calibra[j][i] + readMux(i);
}
}
//Print averages
for(byte j = 0; j < 15; j ++){
writeMux(j);
for(byte i = 0; i < 15; i ++){
calibra[j][i] = calibra[j][i]/50;
if(calibra[j][i] < minsensor)
minsensor = calibra[j][i];
Serial.print(calibra[j][i]);
Serial.print("\t");
}
Serial.println();
}
Serial.println();
Serial.print("Minimum Value: ");
Serial.println(minsensor);
Serial.println();
establishContact();
digitalWrite(COL_pin, LOW);
}
void loop(){
//Loop through and read all 16 values
//Reports back Value at channel 6 is: 346
if (Serial.available() > 0){
inByte = Serial.read();
if(inByte == 'A'){
for(int j = 14; j >= 0; j--){
writeMux(j);
for(int i = 0; i < 15; i++){
valor = readMux(i);
//Saturation sensors
int limsup = 450;
if(valor > limsup)
valor = limsup;
if(valor < calibra[j][i])
valor = calibra[j][i];
valor = map(valor,minsensor, limsup,1,254);
if(valor < 150)
valor = 0;
if(valor > 254)
valor = 254;
Serial.write(valor);
digitalWrite(COL_pin,!digitalRead(COL_pin));
}
}
}
}
}
int readMux(byte channel){
byte controlPin[] = {s0, s1, s2, s3};
//loop through the 4 sig
for(int i = 0; i < 4; i ++){
digitalWrite(controlPin[i], muxChannel[channel][i]);
}
//read the value at the SIG pin
int val = analogRead(SIG_pin);
//return the value
return val;
}
void writeMux(byte channel){
byte controlPin[] = {w0, w1, w2, w3};
//loop through the 4 sig
for(byte i = 0; i < 4; i ++){
digitalWrite(controlPin[i], muxChannel[channel][i]);
}
}
void establishContact() {
while (Serial.available() <= 0) {
Serial.print('A'); // send a capital A
delay(300);
}
}
#include <Arduino.h>
// First multiplexer (connected to A0) - handles rows and second set of columns
const byte mux1_s0 = A4; // Control pins for first multiplexer
const byte mux1_s1 = A3;
const byte mux1_s2 = A2;
const byte mux1_s3 = A1;
const byte mux1_sig = A0; // Signal pin
// Second multiplexer (connected to D8) - handles first set of columns
const byte mux2_s0 = 12; // Control pins for second multiplexer
const byte mux2_s1 = 11;
const byte mux2_s2 = 10;
const byte mux2_s3 = 9;
const byte mux2_sig = 8; // Signal pin
// Mux channel configuration
const boolean muxChannel[16][4] = {
{0, 0, 0, 0}, // Channel 0
{1, 0, 0, 0}, // Channel 1
{0, 1, 0, 0}, // Channel 2
{1, 1, 0, 0}, // Channel 3
{0, 0, 1, 0}, // Channel 4
{1, 0, 1, 0}, // Channel 5
{0, 1, 1, 0}, // Channel 6
{1, 1, 1, 0}, // Channel 7
{0, 0, 0, 1}, // Channel 8
{1, 0, 0, 1}, // Channel 9
{0, 1, 0, 1}, // Channel 10
{1, 1, 0, 1}, // Channel 11
{0, 0, 1, 1}, // Channel 12
{1, 0, 1, 1}, // Channel 13
{0, 1, 1, 1}, // Channel 14
{1, 1, 1, 1} // Channel 15
};
// Function declarations
int readMux1(byte channel);
int readMux2(byte channel);
void setup() {
// Configure pins for first multiplexer
pinMode(mux1_s0, OUTPUT);
pinMode(mux1_s1, OUTPUT);
pinMode(mux1_s2, OUTPUT);
pinMode(mux1_s3, OUTPUT);
pinMode(mux1_sig, INPUT);
// Configure pins for second multiplexer
pinMode(mux2_s0, OUTPUT);
pinMode(mux2_s1, OUTPUT);
pinMode(mux2_s2, OUTPUT);
pinMode(mux2_s3, OUTPUT);
pinMode(mux2_sig, INPUT);
Serial.begin(115200);
Serial.println("Starting row readings test...");
Serial.println("Reading channels C0, C1, C2 from first multiplexer");
Serial.println();
}
void loop() {
// TESTING ROWS CODE 🚨
for (int row = 0; row < 3; row++) {
int rowValue = readMux1(row); // Read channels 0, 1, and 2
Serial.print("Row ");
Serial.print(row);
Serial.print(": ");
Serial.print(rowValue);
Serial.print("\t");
}
Serial.println(); // New line after printing all rows
delay(500); // Wait half a second before next reading
}
// Read from first multiplexer (connected to A0)
int readMux1(byte channel) {
byte controlPin[] = {mux1_s0, mux1_s1, mux1_s2, mux1_s3};
for (int i = 0; i < 4; i++) {
digitalWrite(controlPin[i], muxChannel[channel][i]);
}
return analogRead(mux1_sig);
}
// Read from second multiplexer (connected to D8)
int readMux2(byte channel) {
byte controlPin[] = {mux2_s0, mux2_s1, mux2_s2, mux2_s3};
for (int i = 0; i < 4; i++) {
digitalWrite(controlPin[i], muxChannel[channel][i]);
}
return analogRead(mux2_sig);
}
// Example mapping function
int mapPressure(int rawValue, int row, int col) {
// Define your calibration values
const int NO_PRESSURE = 100; // Example value when no pressure
const int MAX_PRESSURE = 900; // Example value at maximum pressure
// Map the raw value to 0-20 range
int mappedValue = map(rawValue,
NO_PRESSURE,
MAX_PRESSURE,
0,
20);
// Constrain to ensure we stay in range
mappedValue = constrain(mappedValue, 0, 20);
return mappedValue;
}
void sendMatrixData() {
Serial.print("{");
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 18; col++) {
int value = // ... your reading code ...
int mappedValue = mapPressure(value, row, col);
Serial.print("\"");
Serial.print(row);
Serial.print(",");
Serial.print(col);
Serial.print("\":");
Serial.print(mappedValue);
if (!(row == 2 && col == 17)) {
Serial.print(",");
}
}
}
Serial.println("}");
}
We're using this arduino code to test 3 rows and 2 columns of our mat. We're debugging, and therefore no longer using multiplexers for the moment.
I remind you that our structure is writing to copper rows, then there's a layer of velostat and lastly, one of copper columns.
The following arduino pins need to be turned on HIGH, one at a time to HIGH digital out:
D2 -> first column
D3 -> second column
They will be checked across the analog rows to check for values. The following arduino pins are connected and we need to analog read them:
A0 -> top row
A1 -> middle row
A2 -> bottom row
We need to basically cycle through them using 2 for loops that are nested.
Help us write the code for this, so we can:
1. Calibrate - come up with average values so we can see a difference once we touch our mat
2. See on the serial monitor the values for all 6 sensors (3 rows x 2 cols).
////////////// TEST READINGS FOR 3 ROWS AND 2 COLS ///////////////
#include <Arduino.h>
// Pin definitions
const int ROW_PINS[] = {A0, A1, A2}; // Analog input pins for rows
const int COL_PINS[] = {2, 3}; // Digital output pins for columns
const int NUM_ROWS = 3;
const int NUM_COLS = 2;
// Arrays to store sensor values
int currentValues[3][2]; // Current readings
int baselineValues[3][2]; // Calibration baseline
const int NUM_CALIBRATION_SAMPLES = 20;
// Function declarations
void calibrate();
void readMat();
void setup() {
// Init Serial
Serial.begin(9600); // prev using 112500 baud rate
// We need to loop through each of the 3 rows
// then set row 0 as Analog Read
// then set column 0 as HIGH digital out
// then repeat the process using the 2 nested for loops
// Set pin modes
for (int r = 0; r < NUM_ROWS; r++) {
pinMode(ROW_PINS[r], INPUT); // Analog
// Pin value still needs to be read later on.
// This just sets pin X as Input or Output
}
for (int c = 0; c < NUM_COLS; c++) {
pinMode(COL_PINS[c], OUTPUT);
digitalWrite(COL_PINS[c], LOW); // Start with all columns LOW
}
delay(1000); // Add a delay to allow serial to initialize
Serial.println("Starting up..."); // Add this to verify serial is working
// Initial calibration
calibrate();
}
void calibrate() {
Serial.println("Starting calibration...");
// Reset baseline values
for (int r = 0; r < NUM_ROWS; r++) {
for (int c = 0; c < NUM_COLS; c++) {
baselineValues[r][c] = 0;
}
}
// Take multiple samples
for (int sample = 0; sample < NUM_CALIBRATION_SAMPLES; sample++) {
for (int c = 0; c < NUM_COLS; c++) {
// Activate one column at a time
digitalWrite(COL_PINS[c], HIGH);
delay(10); // Small delay for stability
// Read all rows for this column
for (int r = 0; r < NUM_ROWS; r++) {
baselineValues[r][c] += analogRead(ROW_PINS[r]);
// Creating a sum
}
digitalWrite(COL_PINS[c], LOW);
// setting the column pins back to LOW, as we're done with calibration
}
}
// Calculate averages
for (int r = 0; r < NUM_ROWS; r++) {
for (int c = 0; c < NUM_COLS; c++) {
baselineValues[r][c] /= NUM_CALIBRATION_SAMPLES;
Serial.print("Baseline R");
Serial.print(r);
Serial.print("C");
Serial.print(c);
Serial.print(": ");
Serial.println(baselineValues[r][c]);
}
}
Serial.println("Calibration complete!");
}
int inByte = 0; // Initializing inByte and setting to 0
void readMat() {
// Read all sensors
for (int c = 0; c < NUM_COLS; c++) {
// Activate one column at a time
digitalWrite(COL_PINS[c], HIGH);
delay(10); // Small delay for stability
// Read all rows for this column
for (int r = 0; r < NUM_ROWS; r++) {
currentValues[r][c] = analogRead(ROW_PINS[r]);
}
digitalWrite(COL_PINS[c], LOW);
}
// Print the readings
Serial.println("\n--- Current Readings ---");
for (int r = 0; r < NUM_ROWS; r++) {
for (int c = 0; c < NUM_COLS; c++) {
int difference = currentValues[r][c] - baselineValues[r][c];
Serial.print("R");
Serial.print(r);
Serial.print("C");
Serial.print(c);
Serial.print(": ");
Serial.print(currentValues[r][c]);
Serial.print(" (diff: ");
Serial.print(difference);
Serial.print(") ");
}
Serial.println();
}
}
void loop() {
if (Serial.available() > 0) {
int inByte = Serial.read();
if (inByte == '0') { // Changed to char '0' (ASCII 48)
readMat();
} else if (inByte == 'c') { // Added option to recalibrate
calibrate();
}
}
}
* Executing task: platformio device monitor --environment nano_33_iot
--- Terminal on /dev/cu.usbmodem1101 | 9600 8-N-1
--- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at https://bit.ly/pio-monitor-filters
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
Starting up...
Starting calibration...
Baseline R0C0: 5
Baseline R0C1: 1
Baseline R1C0: 5
Baseline R1C1: 4
Baseline R2C0: 3
Baseline R2C1: 2
Calibration complete!
let sensorValues = []; // Array to store sensor readings
let ripples = [];
let lotuses = [];
let caustics = [];
let serial; // WebSerial object
let port; // Port object
let reader; // Reader for serial data
let decoder = new TextDecoder(); // Decoder for incoming serial data
function setup() {
createCanvas(800, 600);
noSmooth();
// Initialize caustics grid
for (let i = 0; i < width; i += 20) {
for (let j = 0; j < height; j += 20) {
caustics.push({ x: i, y: j, offset: random(TWO_PI) });
}
}
// Create a button to connect to the serial port
let connectButton = createButton("Connect to Serial");
connectButton.position(10, 10);
connectButton.mousePressed(connectToSerial);
}
function draw() {
background(0, 0, 150); // Deep blue water
// Draw caustics
drawCaustics();
// Draw and update all ripples
for (let i = ripples.length - 1; i >= 0; i--) {
let ripple = ripples[i];
drawRipple(ripple.x, ripple.y, ripple.size);
ripple.size += 2;
if (ripple.size > 100) {
lotuses.push({ x: ripple.x, y: ripple.y, frame: 0 });
ripples.splice(i, 1);
}
}
// Draw and animate blooming lotuses
for (let lotus of lotuses) {
drawBloomingLotus(lotus.x, lotus.y, lotus.frame);
if (lotus.frame < 30) lotus.frame++;
}
// Use sensor values to create ripples
if (sensorValues.length > 0) {
for (let r = 0; r < sensorValues.length; r++) {
for (let c = 0; c < sensorValues[r].length; c++) {
if (sensorValues[r][c] > 100) {
let x = map(c, 0, sensorValues[r].length, 0, width);
let y = map(r, 0, sensorValues.length, 0, height);
ripples.push({ x, y, size: 10 });
}
}
}
}
}
async function connectToSerial() {
// Request a port and open a connection
try {
port = await navigator.serial.requestPort();
await port.open({ baudRate: 9600 });
serial = port.readable.getReader();
// Start reading data from the serial port
readSerial();
} catch (err) {
console.error("Error connecting to serial:", err);
}
}
async function readSerial() {
while (port.readable) {
try {
const { value, done } = await serial.read();
if (done) {
serial.releaseLock();
break;
}
// Decode the data and process it
const data = decoder.decode(value);
processSerialData(data.trim());
} catch (err) {
console.error("Error reading serial data:", err);
break;
}
}
}
function processSerialData(data) {
// Parse CSV data from Arduino
let rows = data.split("\n");
sensorValues = rows.map((row) =>
row.split(",").map((val) => parseInt(val.trim()))
);
}
function drawRipple(x, y, size) {
noFill();
stroke(255, 255, 255, 100);
strokeWeight(2);
ellipse(x, y, size, size);
}
function drawBloomingLotus(x, y, frame) {
push();
translate(x, y);
let scaleAmount = map(frame, 0, 30, 0, 1);
noStroke();
fill(255, 182, 193);
for (let angle = 0; angle < TWO_PI; angle += PI / 6) {
let petalX = cos(angle) * 30 * scaleAmount;
let petalY = sin(angle) * 30 * scaleAmount;
ellipse(petalX, petalY, 20 * scaleAmount);
}
fill(255, 105, 180);
ellipse(0, 0, 20 * scaleAmount);
pop();
}
function drawCaustics() {
noStroke();
fill(255, 255, 255, 30);
for (let caustic of caustics) {
let wave = sin(frameCount * 0.05 + caustic.offset) * 5;
ellipse(caustic.x + wave, caustic.y + wave, 15, 15);
}
}