Oligonucleotide synthesizer

An oligonucleotide synthesizer is a device used in biochemistry to assemble DNA and/or RNA synthetically. One way to do so is by using a DNA electrowetter device, which is essentially a complex of electrodes that are used to alternate charges to move a droplet containing nucleotide and reagents from one electrode to another. This is what I have been working on with arduino, using an arduino Uno and and SD card reader to take a text file containing a string of nucleotide characters like A, G, C, T, or U, and activate different solenoid valves on reading the corresponding letters. So far, the electrode portion of my device is working, however the solenoid valves wont activate, and the serial monitor data that I could find on the IDE software wont display on the LCD screen.
The biggest issue is that I get the correct electrode response on the serial monitor, however the error: "card failed to initialize" always comes up with it.

Here is my full code:



// PINS
/* MIT Open-Source LICENSE:
*
* Copyright 2018 Charles Grassin
* 
* 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.
*/

// PINS
#define PIN_FEEDBACK (A0)
#define PIN_LE (A1)
#define PIN_CLK (A2)
#define PIN_BL (A3)
#define PIN_DI (A4)
#define PIN_SWA (12)
#define PIN_SWB (11)
#include <Arduino.h>
#include <stdint.h>
#include <SD.h>
#include <SPI.h>
#include <AFMotor.h>
#define MAX_LINE_LEN 80
#include <LiquidCrystal.h>

int solenoidPin1 = 11;
int solenoidPin2 = 11;
int solenoidPin3 = 3;
int solenoidPin4 = 3;
int solenoidPin5 = 5;
int solenoidPin6 = 5;
int solenoidPin7 = 6;
int solenoidPin8 = 6;


// SETTINGS
#define ELECTRODE_ARRAY_WIDTH 8
#define ELECTRODE_ARRAY_HEIGHT 8
const byte HV507_LOOKUP_TABLE [] = {4,5,6,7,12,13,15,14,20,22,23,21,28,31,30,29,36,39,38,37,47,46,45,44,55,62,61,53,54,63,60,52,51,59,56,49,58,50,57,48,43,42,41,40,34,33,32,35,26,25,24,27,18,16,17,19,9,8,10,11,0,1,2,3};
#define BAUD_RATE 115200
#define SERIAL_BUFFER_LENGTH 10
#define TEST_PROG

// VARS
bool electrodes[ELECTRODE_ARRAY_WIDTH][ELECTRODE_ARRAY_HEIGHT];
char serialBuffer[SERIAL_BUFFER_LENGTH];
uint8_t currentIndex=0;

// Electrode array code
void clearElectrodes() {
  for (int x = 0; x <ELECTRODE_ARRAY_WIDTH ; x++) 
    for (int y = 0; y <ELECTRODE_ARRAY_HEIGHT ; y++) 
      setElectrode(x,y,false);
}

void setElectrodes() {
  for (int x = 0; x <ELECTRODE_ARRAY_WIDTH ; x++) 
    for (int y = 0; y <ELECTRODE_ARRAY_HEIGHT ; y++) 
    setElectrode(x,y,true);
}

void setElectrode(int x,int y,bool state) {
  if(state != electrodes[x][y]) {
    electrodes[x][y]=state;
    sendElectrode(x,y);
  }
}

void sendElectrode(int x,int y){
  Serial.print(x);
  Serial.print(",");
  Serial.print(y);
  Serial.print(",");
  Serial.print(electrodes[x][y]);
  Serial.print("\n");
}

// Refer to HV507 datasheet
void writeHV507() {
  digitalWrite(PIN_LE, LOW);
  digitalWrite(PIN_CLK, LOW);
   
  for (int i = 0; i <64 ; i++) {
    digitalWrite(PIN_DI,electrodes[HV507_LOOKUP_TABLE[i]%8][HV507_LOOKUP_TABLE[i]/8]);
    digitalWrite(PIN_CLK, HIGH);
    digitalWrite(PIN_CLK, LOW);
  }

  digitalWrite(PIN_LE, HIGH);
  digitalWrite(PIN_LE, LOW);
}

void setup() {
  Serial.begin(BAUD_RATE);
  
  pinMode(PIN_LE, OUTPUT);
  digitalWrite(PIN_LE,LOW);
  pinMode(PIN_CLK, OUTPUT);
  digitalWrite(PIN_CLK,LOW);
  pinMode(PIN_BL, OUTPUT);
  digitalWrite(PIN_BL,HIGH);
  pinMode(PIN_DI, OUTPUT);
  digitalWrite(PIN_DI,LOW);
  
  pinMode(PIN_SWA, INPUT_PULLUP);
  pinMode(PIN_SWB, INPUT_PULLUP);

  clearElectrodes();
  writeHV507();
  digitalWrite(PIN_BL,HIGH);

  for (int x = 0; x <ELECTRODE_ARRAY_WIDTH ; x++) 
    for (int y = 0; y <ELECTRODE_ARRAY_HEIGHT ; y++) 
      sendElectrode(x,y);
const int cs = 4;
  if (!SD.begin(cs)) 
 {
    Serial.println("Card failed to initialize, or not present");
  
    return;
  }
  Serial.println("card initialized.");
  
  // open the file named ourfile.txt
  
  File myfile = SD.open("ourfile.txt");

  // if the file is available, read the file
  if (myfile) 
  {
    while (myfile.available())
    {
      Serial.write(myfile.read());
    }
    myfile.close();
  }  
  // if the file cannot be opened give error report
  else {
    Serial.println("error opening the text file");
  } 
  Serial.print("Initializing card...");
  pinMode(53, OUTPUT);
     pinMode(solenoidPin1, OUTPUT);
 pinMode(solenoidPin2, OUTPUT);
 pinMode(solenoidPin3, OUTPUT);
 pinMode(solenoidPin4, OUTPUT);
 pinMode(solenoidPin5, OUTPUT);
 pinMode(solenoidPin6, OUTPUT);
 pinMode(solenoidPin7, OUTPUT);
 pinMode(solenoidPin8, OUTPUT);
 
}

#ifdef TEST_PROG
  int step = 0;
  long unsigned int lastT = millis();
#endif

void loop() {
      {



  if(serialReadCommand()){
    writeHV507();
  }

  // Test program
  #ifdef TEST_PROG
    if(millis()-lastT>500){
      lastT = millis();
      switch(step){
        case 0: setElectrode(4,3,false);
        setElectrode(4,4,true);
        break;
        case 1:setElectrode(4,4,false);
        setElectrode(4,5,true);
        break;
        case 2:setElectrode(4,5,false);
        setElectrode(5,5,true);
        break;
        case 3:setElectrode(5,5,false);
        setElectrode(5,4,true);
        break;
        case 4:setElectrode(5,4,false);
        setElectrode(5,3,true);
        break;
        case 5:setElectrode(5,3,false);
        setElectrode(4,3,true);
        break;
        case 6:setElectrode(3,4,true);
        setElectrode(4,3,true);      }
      writeHV507();
      }
      writeHV507();
      step ++;
      if(step > 5) step = 0;
      
    }
  #endif
  if (char Str2[8] = {isAlpha('A'||'a')})
 {
digitalWrite(solenoidPin1, HIGH); //Switch Solenoid ON
  delay(1000); //Wait 1 Second
digitalWrite(solenoidPin1, LOW); //Switch Solenoid ON
delay(1000); //Wait 1 Second
digitalWrite(solenoidPin6, HIGH);
delay(1000);
digitalWrite(solenoidPin6, LOW);
digitalWrite(solenoidPin7, HIGH);
delay(1000);
digitalWrite(solenoidPin7, LOW);
}

if (char Str2[8] = {isAlpha('G'||'g')})
 {
    digitalWrite(solenoidPin2, HIGH); //Switch Solenoid ON
  delay(1000); //Wait 1 Second
digitalWrite(solenoidPin2, LOW); //Switch Solenoid ON
delay(1000); //Wait 1 Second
digitalWrite(solenoidPin6, HIGH);
delay(1000);
digitalWrite(solenoidPin6, LOW);
digitalWrite(solenoidPin7, HIGH);
delay(1000);
digitalWrite(solenoidPin7, LOW);
}
if (char Str2[8] = {isAlpha('C'||'c')})
 {
    digitalWrite(solenoidPin3, HIGH); //Switch Solenoid ON
  delay(1000); //Wait 1 Second
digitalWrite(solenoidPin3, LOW); //Switch Solenoid ON
delay(1000); //Wait 1 Second
digitalWrite(solenoidPin6, HIGH);
delay(1000);
digitalWrite(solenoidPin6, LOW);
digitalWrite(solenoidPin7, HIGH);
delay(1000);
digitalWrite(solenoidPin7, LOW);
  }
  
if (char Str2[8] = {isAlpha('T'||'t')})
 {
   digitalWrite(solenoidPin4, HIGH); //Switch Solenoid ON
  delay(1000); //Wait 1 Second
digitalWrite(solenoidPin4, LOW); //Switch Solenoid ON
delay(1000); //Wait 1 Second
digitalWrite(solenoidPin6, HIGH);
delay(1000);
digitalWrite(solenoidPin6, LOW);
digitalWrite(solenoidPin7, HIGH);
delay(1000);
digitalWrite(solenoidPin7, LOW);
  }
if (char Str2[8] = {isAlpha('U'||'u')})
 {
  digitalWrite(solenoidPin5, HIGH); //Switch Solenoid ON
  delay(1000); //Wait 1 Second
digitalWrite(solenoidPin5, LOW); //Switch Solenoid ON
delay(1000); //Wait 1 Second
digitalWrite(solenoidPin6, HIGH);
delay(1000);
digitalWrite(solenoidPin6, LOW);
digitalWrite(solenoidPin7, HIGH);
delay(1000);
digitalWrite(solenoidPin7, LOW);
}
  if (char Str2[8] = {'\0'}){
     digitalWrite(solenoidPin8, HIGH); //Switch Solenoid ON
  delay(1000); //Wait 1 Second
digitalWrite(solenoidPin8, LOW); //Switch Solenoid ON
delay(1000); //Wait 1 Second
  }

}


uint8_t serialReadCommand(){
  uint8_t number_of_commands = 0;
  while (Serial.available() > 0) {
    char recieved = Serial.read();

    serialBuffer[currentIndex] = recieved;
    currentIndex ++;
    if (currentIndex >= SERIAL_BUFFER_LENGTH) {
      // Invalid command: command is too long
      currentIndex = 0;
    }

    if (recieved == '\n' || recieved == '\r') {
      // Close string
      serialBuffer[currentIndex-1] = '\0';
      
      // Clear buffer for next serial transaction
      currentIndex = 0; 

      // Split the string
      char* commaIndex = strchr(serialBuffer, ',');
      if (commaIndex==NULL) {
        // Invalid command: command is malformed
        continue;
      }
      commaIndex[0] = '\0';
      char* secondCommaIndex = strchr(commaIndex+1, ',');
      if (secondCommaIndex==NULL) {
        // Invalid command: command is malformed
        continue;
      }
      secondCommaIndex[0] = '\0';

      int x = atoi(serialBuffer);
      int y = atoi(commaIndex+1);
      if(x<0 || x>=ELECTRODE_ARRAY_WIDTH || y<0 || y>=ELECTRODE_ARRAY_HEIGHT){
        // Invalid command: out of bound
        continue;
      }
      
      setElectrode(x,y,strcmp(secondCommaIndex+1,"0")!=0);
      number_of_commands += 1;
    }
  }
  return number_of_commands;
}

/*void btnMatrixTest(){
  for (int y = 0; y <ELECTRODE_ARRAY_WIDTH ; y++) 
    for (int x = 0; x <ELECTRODE_ARRAY_HEIGHT ; x++){
      clearElectrodes();
      electrodes[x][y]=true;
      writeHV507();
      Serial.print(x);
      Serial.print("\t");
      Serial.println(y);
      delay(500);
      while(digitalRead(PIN_SWA)){}
    }
}*/

Here is my circuit (excluding electrode wires as they are functional)

I couldn't include the LCD screen since the circuit software didn't have the correct one, being a small 4 pin (GND, VCC, SCL, SDA) screen for arduino.
Are there any suggestions as to why this isn't working?

Why more than one Serial begin?

Did you read and act on the comments on your other topic?
Why not?
Have you considered arrays?
Why not?

I have considered arrays but I had the same Issue. However, granted that this was when my code was a bit different. Do you think that I should try to implement them again ? In the past, I had done so like this:

void writeData(uint64_t data) {
  char* arr = (char*) &data;
  digitalWrite(latchPin, LOW);
  for (int i=0; i < 8; ++i) {
    char c = arr[i];
    shiftOut(dataPin, clockPin, LSBFIRST, c); 
  }
  digitalWrite(latchPin, HIGH);
}

And how was it?

It compiled, but I still saw the "card failed to initialize" error on the serial monitor. The solenoid valves still didn't activate either. Could my "if..else" statements for each valve be the problem blocking any cooperation between the SD text file characters and the solenoid valves? They look like if (char Str2[8] = {isAlpha('A'||'a')}). Or could it just be something that doesn't work with strings?

Can you explain that particular train of thought?

Could it be that the way the if statements are coded that they don't read the strings, and thus don't recognize it as means for activation? I was mostly looking at the Str2[8] bit of each of the if statements, could the use of that be seen by the code as a different parameter or something and so it wont read the characters properly in that respect?

I'm more a hardware guy that software. And if the wiring is as you've shown I can see some reasons for issues.

  1. Have you tested each function one at a time to see if the code for that device is working? If not that is the first step you should make.

  2. neatness doesn't count in electrical circuits (within reason). You should connect all the solenoid grounds and the Plus supply to the solenoid drivers to a single point. Perhaps grouped up on the far right of the board you've shown. Then add a 100µF cap in parallel with a 0.1µF cap across Plus to ground.

  3. Keep the display (not shown but mentioned), the SD card and arduino ground to the left.

  4. Introduce the power supply between them at the 100µF//0.1µF capacitors.

Do you mean physically congregate the solenoid valves to the left side of the breadboard with their individual diodes and resistors, as well as the 100µF cap and capacitor? Or should they share these components? It was hard to visualize this so I tried my best to make a model, if I did something wrong with your directions I apologize and would appreciate the feedback.

What do you THINK this statement doing?

(Whatever you think, I can assure you that it is not doing that.)

I thought that it was taking the read string character A or a and using it to activate the solenoid valve. How do you think I should have done it instead?

Text based descriptions are only marginally useful. Below are two sketches showing what I suggest.
The first shows the concept
The second shows how one might lay it out on a solderless board. The drivers can be above where I call out Solenoid power BUT they must be on the "Power" side of the board. Here the key is to keep things separated.

In your post #9 sketch: I realize you were just trying to understand my crude description but a thing to know is the SD should be on the right side of your board, keeping all their wires close to other low power wires and away (physically) from the power (solenoid) circuits.

Since this is in setup() , if you are seeing the message a lot, that indicates the sketch is crashing. A power issue is then likely. anyway, if you are having other problems with the SD card reader then test it separately in a stand alone sketch.

This is not consistent with the Uno you have illustrated.

Hi,
This project seems to be University or other tertiary level project.
WHY are you using FRITZY?
Can you please provide a proper drawn SCHEMATIC of your project.
Please include ALL power supplies.

I would have thought at this level, schematic diagrams were a must.

All I see in your Fritzy is a mass of wires with no component labels or pin names.
I have ignored the image in post #1.
In this image below;


What are the transistors/mosfets, what are their pinouts.
(Close look at the transistors shows that they are supposed to be P-CH devices, are they????)

What value are the caps?
There are no pin lables on the SD card module.

When you submit your project, will you be using Fritzy images?
Sorry but from the project title and its proposed use, I would expect a better circuit presentation.
Even a carefully hand drawn diagram and posting an image of it would be better.

Tom.... :grinning: :+1: :coffee: :australia:

Hi,
Have you got some code that JUST reads and writes to the SD card?
If not then start there and establish and prove you can communicate with it, do not have any other hardware connected.
Try the SD library examples to do this.

Have you got some code that JUST activates the solenoid valves?
If not then start there and establish you can activate them, write some code that just turns them ON and OFF every second.
How are you powering the solenoids.

Can you please give us a proper electronic parts list and links to specs/data.

Thanks.. Tom.... :grinning: :+1: :coffee: :australia:

1 Like

Sorry. Not even close. Let's parse it step by step.
if (char Str2[8] = {isAlpha('A'||'a')})

"||" is the boolean 'OR' operator. The two operands get converted to boolean (0->false and non-0 -> true). 'A' becomes 'true' and 'a' becomes true. "true || true" result in true so now you have:
if (char Str2[8] = {isAlpha(true)})

The "isAlpha()" function returns true if the argument is an alphabetic character. Since 'true' converts to the character value 1, true is not an alphabetic character and isAlpha() returns false. Now you have:
if (char Str2[8] = {false})

The "char Str2[8] = {false}" part declares an 8-character array initialized to 'false' and the array is never used. I think this boils down to: if (false) so the body of the 'if' is never executed.

My best guess is:
if (toUpper(serialBuffer[0]) == 'A' )

1 Like

Ok so this statment worked, however it should be noted that the arduino version of it (as opposed to stardard C++) is if (toUpperCase(serialBuffer[0]) == 'A' ).
I have rewired my circuit differently, and It is working, and I have attached the new (single valve and SD card) circuit image below. I will add on the rest of the valves and keep the topic updated.

Uploading: unnamed.jpg...

Sure thing heres the list:
1 Arduino Uno (rev3)
8 small 6-12V solenoid valves
pin wires
N-channel MOFSET 60V 30 A
5V voltage regulators
20K resistors
!A 50V diode rectifiers
100nF ceramic capacitors
Arduino power cord
Arduino USB cord

I used the SD card arduino example library and it helped a lot! I'm not sure what was wrong initially, but the initialization messages ended up showing when I used that code. Thanks for the suggestion!

Can't follow wiring in a picture.
Can you please post a schematic, not a Fitzy?

Thanks.. Tom... :grinning: :+1: :coffee::australia:

I have fixed this pin output, and it worked out. Thanks for pointing that out!