Arduino Basics: ArduinoBasics
Showing posts with label ArduinoBasics. Show all posts
Showing posts with label ArduinoBasics. Show all posts

2 July 2015

NeoPixel Playground


The NeoPixel Digital RGB LED Strip (144 LED/m) is a really impressive product that will have you lighting up your room in next to no time. The 144 individually addressable LEDs packed onto a 1 metre flexible water resistant strip, enables a world of luminescent creativity that will blow your blinking Arduino friends away. The following tutorial will show you how to create an immersive and interactive LED display using an Arduino UNO, a potentiometer and an accelerometer. There will be a total of FIVE LED sequences to keep you entertained or you can create your own !
 
This tutorial was specifically designed to work with the 144 Neopixel Digital RGB LED strip with the ws2812B chipset.

 

Parts Required:

Power Requirements

Before you start any LED strip project, the first thing you will need to think about is POWER. According to the Adafruit website, each individual NeoPixel LED can draw up to 60 milliamps at maximum brightness - white. Therefore the amount of current required for the entire strip will be way more than your Arduino can handle. If you try to power this LED strip directly from your Arduino, you run the risk of damaging not only your Arduino, but your USB port as well. The Arduino will be used to control the LED strip, but the LED strip will need to be powered by a separate power supply. The power supply you choose to use is important. It must provide the correct voltage, and must able to supply sufficient current.
 

Operating Voltage(5V)

The operating voltage of the NeoPixel strip is 5 volts DC. Excessive voltage will damage/destroy your NeoPixels.

Current requirements (8.6 Amps)

OpenLab recommend the use of a 5V 10A power supply. Having more Amps is OK, providing the output voltage is 5V DC. The LEDs will only draw as much current as they need. To calculate the amount of current this 1m strip can draw with all LEDs turned on at full brightness - white:

144 NeoPixel LEDs x 60 mA x 1 m = 8640 mA = 8.64 Amps for a 1 metre strip.

Therefore a 5V 10A power supply would be able to handle the maximum current (8.6 Amps) demanded by a single 1m NeoPixel strip of 144 LEDs.
 
 

Arduino Libraries and IDE


Before you start to hook up any components, upload the following sketch to the Arduino microcontroller. I am assuming that you already have the Arduino IDE installed on your computer. If not, the IDE can be downloaded from here.
 
The FastLED library is useful for simplifying the code for programming the NeoPixels. The latest "FastLED library" can be downloaded from here. I used FastLED library version 3.0.3 in this project.
 
If you have a different LED strip or your NeoPixels have a different chipset, make sure to change the relevant lines of code to accomodate your hardware. I would suggest you try out a few of the FastLED library examples before using the code below, so that you become more familiar with the library, and will be better equipped to make the necessary changes. If you have a single 144 NeoPixel LED/m strip with the ws2812B chipset, then you will not have to make any modifications below (unless you want to).
 

ARDUINO CODE:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318   
    
/* ==================================================================================================================================================
         Project: NeoPixel Playground
Neopixel chipset: ws2812B  (144 LED/m strip)
          Author: Scott C
         Created: 12th June 2015
     Arduino IDE: 1.6.4
         Website: http://arduinobasics.blogspot.com/p/arduino-basics-projects-page.html
     Description: This project will allow you to cycle through and control five LED
                  animation sequences using a potentiometer and an accelerometer
                     Sequence 1:   Cylon with Hue Control                                       Control: Potentiometer only
                     Sequence 2:   Cylon with Brightness Control                                Control: Potentiometer only
                     Sequence 3:   Comet effect with Hue and direction control                  Control: Potentiometer and Accelerometer (Y axis only)
                     Sequence 4:   FireStarter / Rainbow effect with Hue and Direction control  Control: Potentiometer and Accelerometer (Y axis only)
                     Sequence 5:   Digital Spirit Level                                         Control: Accelerometer only (Y axis)
            
                  This project makes use of the FastLED library. Some of the code below was adapted from the FastLED library examples (eg. Cylon routine).
                  The Comet, FireStarter and Digital Spirit Level sequence was designed by ScottC.
                  The FastLED library can be found here: http://fastled.io/
                  You may need to modify the code below to accomodate your specific LED strip. See the FastLED library site for more details.
===================================================================================================================================================== */

//This project needs the FastLED library - link in the description.
#include "FastLED.h"

//The total number of LEDs being used is 144
#define NUM_LEDS 144

// The data pin for the NeoPixel strip is connected to digital Pin 6 on the Arduino
#define DATA_PIN 6

//Initialise the LED array, the LED Hue (ledh) array, and the LED Brightness (ledb) array.
CRGB leds[NUM_LEDS];
byte ledh[NUM_LEDS];
byte ledb[NUM_LEDS];

//Pin connections
const int potPin = A0;      // The potentiometer signal pin is connected to Arduino's Analog Pin 0
const int yPin = A4;        // Y pin on accelerometer is connected to Arduino's Analog Pin 4
                            // The accelerometer's X Pin and the Z Pin were not used in this sketch

//Global Variables ---------------------------------------------------------------------------------
byte potVal;                // potVal:      stores the potentiometer signal value
byte prevPotVal=0;          // prevPotVal:  stores the previous potentiometer value
int LEDSpeed=1;             // LEDSpeed:    stores the "speed" of the LED animation sequence
int maxLEDSpeed = 50;       // maxLEDSpeed: identifies the maximum speed of the LED animation sequence
int LEDAccel=0;             // LEDAccel:    stores the acceleration value of the LED animation sequence (to speed it up or slow it down)
int LEDPosition=72;         // LEDPosition: identifies the LED within the strip to modify (leading LED). The number will be between 0-143.  (Zero to NUM_LEDS-1)
int oldPos=0;               // oldPos:      holds the previous position of the leading LED
byte hue = 0;               // hue:         stores the leading LED's hue value
byte intensity = 150;       // intensity:   the default brightness of the leading LED
byte bright = 80;           // bright:      this variable is used to modify the brightness of the trailing LEDs
int animationDelay = 0;     // animationDelay: is used in the animation Speed calculation. The greater the animationDelay, the slower the LED sequence.
int effect = 0;             // effect:      is used to differentiate and select one out of the four effects
int sparkTest = 0;          // sparkTest:   variable used in the "sparkle" LED animation sequence 
boolean constSpeed = false; // constSpeed:  toggle between constant and variable speed.


//===================================================================================================================================================
// setup() : Is used to initialise the LED strip
//===================================================================================================================================================
void setup() {
    delay(2000);          //Delay for two seconds to power the LEDS before starting the data signal on the Arduino
    FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);                            //initialise the LED strip       
}


//===================================================================================================================================================
// loop() : The Arduino will take readings from the potentiometer and accelerometer to control the LED strip
//===================================================================================================================================================
void loop(){
  readPotentiometer();           
  adjustSpeed();
  constrainLEDs();
 
  switch(effect){
    case 0:                                               // 1st effect : Cylon with Hue control - using Potentiometer 
      cylonWithHueControl();
      break;
      
    case 1:                                               // 2nd effect : Cylon with Brightness control - using Potentiometer
      cylonWithBrightnessControl();
      break;
      
    case 2:                                               // 3rd effect : Comet effect. Hue controlled by potentiometer, direction by accelerometer
      cometEffect();
      break;
      
    case 3:                                               // 4th effect : FireStarter / Rainbow Sparkle effect. Direction controlled by accelerometer, sparkle by potentiometer.
      fireStarter(); 
      break;
    
    case 4:
      levelSense();                                        // 5th effect : LevelSense - uses the accelerometer to create a digital "spirit" level.
      break;
  }
}


//===================================================================================================================================================
// readPotentiometer() : Take a potentiometer reading. This value will be used to control various LED animations, and to choose the animation sequence to display.
//===================================================================================================================================================
void readPotentiometer(){
  //Take a reading from the potentiometer and convert the value into a number between 0 and 255
  potVal = map(analogRead(potPin), 0, 1023 , 0, 255);
  
  // If the potentiometer reading is equal to zero, then move to the next effect in the list.
  if(potVal==0){
    if(prevPotVal>0){   // This allows us to switch effects only when the potentiometer reading has changed to zero (from a positive number). Multiple zero readings will be ignored.
      prevPotVal = 0;   // Set the prev pot value to zero in order to ignore replicate zero readings.
      effect++;         // Go to the next effect.
      if(effect>4){
        effect=0;       // Go back to the first effect after the fifth effect.
      }
    }
  }
  prevPotVal=potVal;    // Keep track of the previous potentiometer reading
}


//===================================================================================================================================================
// adjustSpeed() : use the Y axis value of the accelerometer to adjust the speed and the direction of the LED animation sequence
//===================================================================================================================================================
void adjustSpeed(){
  // Take a reading from the Y Pin of the accelerometer and adjust the value so that 
  // positive numbers move in one direction, and negative numbers move in the opposite diraction. 
  // We use the map function to convert the accelerometer readings, and the constrain function to ensure that it stays within the desired limits
  // The values of 230 and 640 were determined by trial and error and are specific to my accelerometer. You will need to adjust these numbers to suit your module.
  
  LEDAccel = constrain(map(analogRead(yPin), 230, 640 , maxLEDSpeed, -maxLEDSpeed),-maxLEDSpeed, maxLEDSpeed);
  
  
  // If the constSpeed variable is "true", then make sure that the speed of the animation is constant by modifying the LEDSpeed and LEDAccel variables.
  if(constSpeed){
    LEDAccel=0; 
    if(LEDSpeed>0){
      LEDSpeed = maxLEDSpeed/1.1;     // Adjust the LEDSpeed to half the maximum speed in the positive direction
    } 
    if (LEDSpeed<0){
      LEDSpeed = -maxLEDSpeed/1.1;    // Adjust the LEDSpeed to half the maximum speed in the negative direction
    }
  } 
 
  // The Speed of the LED animation sequence can increase (accelerate), decrease (decelerate) or stay the same (constant speed)
  LEDSpeed = LEDSpeed + LEDAccel;                        
  
  //The following lines of code are used to control the direction of the LED animation sequence, and limit the speed of that animation. 
  if (LEDSpeed>0){
    LEDPosition++;                                       // Illuminate the LED in the Next position
    if (LEDSpeed>maxLEDSpeed){
      LEDSpeed=maxLEDSpeed;                              // Ensure that the speed does not go beyond the maximum speed in the positive direction
    }
  }
  
  if (LEDSpeed<0){
    LEDPosition--;                                       // Illuminate the LED in the Prior position
    if (LEDSpeed<-maxLEDSpeed){
      LEDSpeed = -maxLEDSpeed;                           // Ensure that the speed does not go beyond the maximum speed in the negative direction
    }
  }
}


//===================================================================================================================================================
// constrainLEDs() : This ensures that the LED animation sequence remains within the boundaries of the various arrays (and the LED strip)
//                   and it also creates a "bouncing" effect at both ends of the LED strip.
//===================================================================================================================================================
void constrainLEDs(){
  LEDPosition = constrain(LEDPosition, 0, NUM_LEDS-1);    // Make sure that the LEDs stay within the boundaries of the LED strip
  if(LEDPosition == 0 || LEDPosition == NUM_LEDS-1) {
    LEDSpeed = (LEDSpeed * -0.9);                         // Reverse the direction of movement when LED gets to end of strip. This creates a bouncing ball effect.
  }
}



//===================================================================================================================================================
// cylonWithHueControl() :  This is the 1st LED effect. The cylon colour is controlled by the potentiometer. The speed is constant.
//===================================================================================================================================================
void cylonWithHueControl(){
      constSpeed = true;                                  // Make the LED animation speed constant
      showLED(LEDPosition, potVal, 255, intensity);       // Illuminate the LED
      fadeLEDs(8);                                        // Fade LEDs by a value of 8. Higher numbers will create a shorter tail.
      setDelay(LEDSpeed);                                 // The LEDSpeed is constant, so the delay is constant
}


//===================================================================================================================================================
// cylonWithBrightnessControl() : This is the 2nd LED effect. The cylon colour is red (hue=0), and the brightness is controlled by the potentiometer
//===================================================================================================================================================
void cylonWithBrightnessControl(){
      constSpeed = true;                                  // Make speed constant
      showLED(LEDPosition, 0, 255, potVal);               // Brightness is controlled by potentiometer.
      fadeLEDs(16);                                       // Fade LEDs by a value of 16
      setDelay(LEDSpeed);                                 // The LEDSpeed is constant, so the delay is constant
}


//===================================================================================================================================================
// cometEffect() :  This is the 3rd LED effect. The random brightness of the trailing LEDs produces an interesting comet-like effect.
//===================================================================================================================================================
void cometEffect(){
      constSpeed = false;                                  // The speed will be controlled by the slope of the accelerometer (y-Axis)
      showLED(LEDPosition, potVal, 255, intensity);        // Hue will change with potentiometer.
      
      //The following lines create the comet effect 
      bright = random(50, 100);                            // Randomly select a brightness between 50 and 100
      leds[LEDPosition] = CHSV((potVal+40),255, bright);   // The trailing LEDs will have a different hue to the leading LED, and will have a random brightness
      fadeLEDs(8);                                         // This will affect the length of the Trailing LEDs
      setDelay(LEDSpeed);                                  // The LEDSpeed will be affected by the slope of the Accelerometer's y-Axis
}


//===================================================================================================================================================
// fireStarter() : This is the 4th LED effect. It starts off looking like a ball of fire, leaving a trail of little fires. But as you
//                 turn the potentiometer, it becomes more like a shooting star with a rainbow-sparkle trail.
//===================================================================================================================================================
void fireStarter(){
      constSpeed = false;                                  // The speed will be controlled by the slope of the accelerometer (y-Axis)
      ledh[LEDPosition] = potVal;                          // Hue is controlled by potentiometer
      showLED(LEDPosition, ledh[LEDPosition], 255, intensity); 
      
      //The following lines create the fire starter effect
      bright = random(50, 100);                            // Randomly select a brightness between 50 and 100
      ledb[LEDPosition] = bright;                          // Assign this random brightness value to the trailing LEDs
      sparkle(potVal/5);                                   // Call the sparkle routine to create that sparkling effect. The potentiometer controls the difference in hue from LED to LED.
      fadeLEDs(1);                                         // A low number creates a longer tail
      setDelay(LEDSpeed);                                  // The LEDSpeed will be affected by the slope of the Accelerometer's y-Axis
}


//===================================================================================================================================================
// levelSense() : This is the 5th and final LED effect. The accelerometer is used in conjunction with the LED strip to create a digital "Spirit" Level.
//                You can use the illuminated LEDs to identify the angle of the LED strip
//===================================================================================================================================================
void levelSense(){
      constSpeed = true;
      LEDPosition = constrain(map(analogRead(yPin), 230, 640, 1, NUM_LEDS-1), 0 , NUM_LEDS-1);
      
      //Jitter correction: this will reduce the amount of jitter caused by the accelerometer reading variability
      if(abs(LEDPosition-oldPos) < 2){
        LEDPosition = oldPos;
      }
      
      //The following lines of code will ensure the colours remain within the red to green range, with green in the middle and red at the ends.
      hue = map(LEDPosition, 0, NUM_LEDS-1, 0, 200);
      if (hue>100){
         hue = 200 - hue;
      }
      
      //Illuminate 2 LEDs next to each other
      showLED(LEDPosition, hue, 255, intensity); 
      showLED(LEDPosition-1, hue, 255, intensity);              
      
      //If the position moves, then fade the old LED positions by a factor of 25 (high numbers mean shorter tail)
      fadeLEDs(25);                               
      oldPos = LEDPosition; 
}


//===================================================================================================================================================
// fadeLEDs(): This function is used to fade the LEDs back to black (OFF) 
//===================================================================================================================================================
void fadeLEDs(int fadeVal){
  for (int i = 0; i<NUM_LEDS; i++){
    leds[i].fadeToBlackBy( fadeVal );
  }
}



//===================================================================================================================================================
// showLED() : is used to illuminate the LEDs 
//===================================================================================================================================================
void showLED(int pos, byte LEDhue, byte LEDsat, byte LEDbright){
  leds[pos] = CHSV(LEDhue,LEDsat,LEDbright);
  FastLED.show();
}


//===================================================================================================================================================
// setDelay() : is where the speed of the LED animation sequence is controlled. The speed of the animation is controlled by the LEDSpeed variable.
//              and cannot go faster than the maxLEDSpeed variable.
//===================================================================================================================================================
void setDelay(int LSpeed){
  animationDelay = maxLEDSpeed - abs(LSpeed);
  delay(animationDelay);
}


//===================================================================================================================================================
// sparkle() : is used by the fireStarter routine to create a sparkling/fire-like effect
//             Each LED hue and brightness is monitored and modified using arrays  (ledh[]  and ledb[])
//===================================================================================================================================================
void sparkle(byte hDiff){
  for(int i = 0; i < NUM_LEDS; i++) {
    ledh[i] = ledh[i] + hDiff;                // hDiff controls the extent to which the hue changes along the trailing LEDs
    
    // This will prevent "negative" brightness.
    if(ledb[i]<3){
      ledb[i]=0;
    }
    
    // The probability of "re-igniting" an LED will decrease as you move along the tail
    // Once the brightness reaches zero, it cannot be re-ignited unless the leading LED passes over it again.
    if(ledb[i]>0){
      ledb[i]=ledb[i]-2;
      sparkTest = random(0,bright);
      if(sparkTest>(bright-(ledb[i]/1.1))){
        ledb[i] = bright;
      } else {
        ledb[i] = ledb[i] / 2;                  
      }
    }
    leds[i] = CHSV(ledh[i],255,ledb[i]);
  }
}


 

NeoPixel Strip connection

The NeoPixel strip is rolled up when you first get it. You will notice that there are wires on both sides of the strip. This allows you to chain LED strips together to make longer strips. The more LEDs you have, the more current you will need. Connect your Arduino and power supply to the left side of the strip, with the arrows pointing to the right side of the strip.
 

Follow the Arrows

The arrows are quite hard to see on this particular LED strip because they are so small, plus they are located right under the thicker part of the NeoPixel weatherproof sheath. I have circled the arrows in RED so that you know where to look:

 


NeoPixel Strip Wires

There are 4 wires coming from either side of the NeoPixel LED strip:
 
  One red wire, one white wire, and two black wires.
 
It doesn't matter which Black wire you use to connect to the power supply (or Arduino) GND. Both black wires appear to be going to the same pin on the LED strip anyway. Use the table below to make the necessary NeoPixel Strip connections to the Arduino and power supply.


Large Capacitor

Adafruit also recommend the use of a large capacitor across the + and - terminals of the LED strip to "prevent the initial onrush of current from damaging the pixels". Adafruit recommends a capacitor that is 1000uF, 6.3V or higher. I used a 4700uF 16V Electrolytic Capacitor.

Resistor on Data Pin

Another recommendation from Adafruit is to place a "300 to 500 Ohm resistor" between the Arduino's data pin and the data input on the first NeoPixel to prevent voltage spikes that can damage the first pixel. I used a 330 Ohm resistor.
 

Powering your Arduino (USB vs Power supply)

You can power your Arduino board via USB cable or via the LED strip power supply.
*** Please note: different power supplies will yield different accelerometer readings. I noticed this when changing the Arduino's power source from USB to LED power supply. My final sketch was designed to eliminate the USB/computer connection, hence I have chosen to power the Arduino via the power supply. The fritzing sketch below shows the Arduino being powered by a power supply only.

**WARNING: If you decide to power your Arduino UNO via a USB cable, please make sure to remove (or disconnect) the wire that goes to the the Arduino VIN pin. The GND connections remain unchanged.


Fritzing Sketch - NeoPixel strip connection


 

Potentiometer connection

The potentiometer will be used to switch between the different LED sequences. When it reads zero, it will switch to the next sequence in the list. It will jump right back to the beginning after the last sequence. The potentiometer is also used to interact with the LEDs (e.g. controlling hue, brightness etc etc).
See the fritzing sketch below to add the potentiometer to this project.



 

Accelerometer connection (Y-axis)

The accelerometer makes the LEDs much more fun and interactive. We will only be using the Y-axis of the accelerometer in this sketch. By tilting the accelerometer from one side to the other, the LEDs react and respond accordingly. The accelerometer is an essential component of the digital spirit level sequence. That's right ! You can use this sketch to create your own spirit level. This digital version can also be used to measure angles !
 
Have a look below to see how to hook up the accelerometer to the Arduino. The Y-axis is connected to the Arduino analog pin 4. If you wanted to use the X and Z axis, connect them to one of the other available analog pins (eg. A3 and A5).




 

Let the fun begin !!

Now that you have the Arduino code uploaded to the Arduino, and have made all of the necessary wire/component connections, it is time to turn on the power supply.
 

Sequence 1: Cylon with Hue control

The LEDs will move from one end of the strip to the other. It should start off as a RED cylon effect. As you turn the potentiometer clockwise, the colour of the LEDs will change and move through the various colours of the rainbow. If the potentiometer reading gets back to zero (fully anti-clockwise), it will move to sequence 2.
 

Sequence 2: Cylon with brightness control

You will see that the LEDs have turned off. The potentiometer readings correlate with the LED brightness. At the start of this sequence, the potentiometer readings will be zero, therefore the brightness will be zero (LEDs turned off). As you turn the potentiometer clockwise, the readings increase, and so will the brightness of the LEDs.
 

Sequence 3: Comet effect with Hue and direction control

This is where the real fun begins. You control the hue of the leading LED with the potentiometer, however the LED will move along the LED strip as though it were affected by gravity. As it hits the end of the LED strip, it will bounce for a while and eventually come to a stop. The more you tilt the accelerometer, the greater the acceleration of the leading LED. The trailing LEDs have an interesting randomised glow, which creates the "comet" effect.
 

Sequence 4: FireStarter / Rainbow effect : Hue and direction control

The initial colours of LEDs in this sequence creates a fire-like animation. As the leading LED moves along the LED strip, it appears to ignite the LEDs in its path, leaving a fire trail behind it. The fire effect is best when you turn the potentiometer clockwise slightly to introduce a small amount of yellow into the mix of colours. As you turn the potentiometer further clockwise, the fire trail turns into a pretty rainbow trail. The accelerometer affects the leading LED in the same way as the previous sequence.
 

Sequence 5: Digital spirit level

This sequence was my original idea for this project, however I thought it would be nice to share some of the other cool effects I created on my journey of discovery. The idea was to make a digital version of a spirit level. I originally wanted the LEDs to represent a spirit level bubble that would "float" according to the vertical/horizontal position of the LED strip. However, as I played around with this sketch, I discovered that it could potentially be used to measure the angle of the strip relative to the horizon. The angle can be determined by the illuminated LED. If the strip is horizontal, the illuminated LEDs will be close to the middle of the strip, and their colour will be green. If the strip is vertical, the illuminated LEDs will be close to end of the strip, and their colour will be red. The colour is just an additional visual indicator.
 


Concluding Comments

The NeoPixel Digital RGB LED strip is a lot of fun. The FastLED library makes for easy programming, and allows you to get up and running really quickly. 144 LEDs on a single strip means you have plenty of room for creative algorithms and lighting effects. Add a few sensors, and "pretty" quickly turns into "awesome" !!
 
This tutorial shows you how to control a "144 NeoPixel per metre Digital RGB LED strip" with an Arduino UNO. Feel free to share your own LED creations in the comments below.



If you like this page, please do me a favour and show your appreciation :

 
Visit my ArduinoBasics Google + page.
Follow me on Twitter by looking for ScottC @ArduinoBasics.
I can also be found on Pinterest and Instagram.
Have a look at my videos on my YouTube channel.


 
 
             

This project would not have been possible without the collaborative effort from OpenLab.
Please visit their site for more cool projects.



However, if you do not have a google profile...
Feel free to share this page with your friends in any way you see fit.

22 June 2015

Send HEX values to Arduino

FIVE MINUTE TUTORIAL

Project Description: Sending Hex values to an Arduino UNO


This simple tutorial will show you how to send Hexadecimal values from a computer to an Arduino Uno. The "Processing" programming language will be used to send the HEX values from the computer when a mouse button is pressed. The Arduino will use these values to adjust the brightness of an LED.



 

Learning Objectives


  • To Send Hexadecimal (Hex) values from a computer to the Arduino
  • Trigger an action based on the press of a mouse button
  • Learn to create a simple Computer to Arduino interface
  • Use Arduino's PWM capabilities to adjust brightness of an LED
  • Learn to use Arduino's analogWrite() function
  • Create a simple LED circuit


 

Parts Required:


Fritzing Sketch


The diagram below will show you how to connect an LED to Digital Pin 10 on the Arduino.
Don't forget the 330 ohm resistor !
 


 
 

Arduino Sketch


The latest version of Arduino IDE can be downloaded here.
 
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
    
/* ==================================================================================================================================================
         Project: 5 min tutorial: Send Hex from computer to Arduino
          Author: Scott C
         Created: 21th June 2015
     Arduino IDE: 1.6.4
         Website: http://arduinobasics.blogspot.com/p/arduino-basics-projects-page.html
     Description: Arduino Sketch used to adjust the brightness of an LED based on the values received
                  on the serial port. The LED needs to be connected to a PWM pin. In this sketch
                  Pin 10 is used, however you could use Pin 3, 5, 6, 9, or 11 - if you are using an Arduino Uno.
===================================================================================================================================================== */

byte byteRead;                   //Variable used to store the byte received on the Serial Port
int ledPin = 10;                 //LED is connected to Arduino Pin 10. This pin must be PWM capable.  

void setup() {
 Serial.begin(9600);             //Initialise Serial communication with the computer
 pinMode(ledPin, OUTPUT);        //Set Pin 10 as an Output pin
 byteRead = 0;                   //Initialise the byteRead variable to zero.
}

void loop() {
  if(Serial.available()) {
    byteRead = Serial.read();     //Update the byteRead variable with the Hex value received on the Serial COM port.
  }
  
  analogWrite(ledPin, byteRead);  //Use PWM to adjust the brightness of the LED. Brightness is determined by the "byteRead" variable.
}    


 


 
 

Processing Sketch


The latest version of the Processing IDE can be downloaded here.
 
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
    
/* ==================================================================================================================================================
         Project: 5 min tutorial: Send Hex from computer to Arduino
          Author: Scott C
         Created: 21th June 2015
  Processing IDE: 2.2.1
         Website: http://arduinobasics.blogspot.com/p/arduino-basics-projects-page.html
     Description: Processing Sketch used to send HEX values from computer to Arduino when the mouse is pressed.
                  The alternating values 0xFF and 0x00 are sent to the Arduino Uno to turn an LED on and off.
                  You can send any HEX value from 0x00 to 0xFF. This sketch also shows how to convert Hex strings
                  to Hex numbers.
===================================================================================================================================================== */

import processing.serial.*;           //This import statement is required for  Serial communication

Serial comPort;                       //comPort is used to write Hex values to the Arduino
boolean toggle = false;               //toggle variable is used to control which hex variable to send  
String zeroHex = "00";                //This "00" string will be converted to 0x00 and sent to Arduino to turn LED off.
String FFHex = "FF";                  //This "FF" string will be converted to 0xFF and sent to Arduino to turn LED on.

void setup(){
    comPort = new Serial(this, Serial.list()[0], 9600);   //initialise the COM port for serial communication at a baud rate of 9600.
    delay(2000);                      //this delay allows the com port to initialise properly before initiating any communication.
    background(0);                    //Start with a black background.
    
}


void draw(){                           //the draw() function is necessary for the sketch to compile 
    //do nothing here                  //even though it does nothing.
}


void mousePressed(){                   //This function is called when the mouse is pressed within the Processing window.
  toggle = ! toggle;                   //The toggle variable will change back and forth between "true" and "false"
  if(toggle){                          //If the toggle variable is TRUE, then send 0xFF to the Arduino
     comPort.write(unhex(FFHex));      //The unhex() function converts the "FF" string to 0xFF
     background(0,0,255);              //Change the background colour to blue as a visual indication of a button press.
  } else {
    comPort.write(unhex(zeroHex));     //If the toggle variable is FALSE, then send 0x00 to the Arduino
    background(0);                     //Change the background colour to black as a visual indication of a button press.
  }
}
    


 

The Video


 


The tutorial above is a quick demonstration of how to convert Hex strings on your computer and send them to an Arduino. The Arduino can use the values to change the brightness of an LED as shown in this tutorial, however you could use it to modify the speed of a motor, or to pass on commands to another module. Hopefully this short tutorial will help you with your project. Please let me know how it helped you in the comments below.

 
 



If you like this page, please do me a favour and show your appreciation :

 
Visit my ArduinoBasics Google + page.
Follow me on Twitter by looking for ScottC @ArduinoBasics.
I can also be found on Pinterest and Instagram.
Have a look at my videos on my YouTube channel.


 
 
             


 
 



However, if you do not have a google profile...
Feel free to share this page with your friends in any way you see fit.

7 May 2015

CH376S USB Read/Write module

Have you ever wondered if there was a way to store and retrieve data from a USB stick with an Arduino UNO? Most people choose SD cards to store their project data, but you may be surprised there IS a way!
IC Station have a nice little module which allows you store and retrieve your Arduino (or other MCU) project data to a USB stick.
 
I am not too sure why USB storage is not widely used in Arduino projects? These modules are not expensive, they have been around for quite a while, and are relatively simple to use. You do not need any libraries to get them to work, however, I must say that documentation for this module is not that easy to find. This site and this document proved to be very useful in my endevour to get this module working, and I hope my tutorial below will help you get started and bridge some of the information gaps.
 
The "CH376S USB read/write module" has a CH376S chip onboard which does most of the hard work for you. All you have to do is send the module some commands from the Arduino and the CH376S chip will do the rest. You can communicate with the module in three different ways:

  • Parallel communication
  • SPI communication
  • and Serial (UART) communication.

This project will show you the connections and code for the Serial (UART) communication method only.


 

Parts Required:

Remove the Jumper

When the CH376S USB module arrives in it's package, it will have a jumper between the TXD pin and GND. You will need to remove this jumper to make the necessary connections between the Arduino UNO and the CH376S USB module.


 

Fritzing Sketch

Please note, that the Arduino Sketch makes use of the Arduino UNO's onboard LED on digital pin 13. The Fritzing sketch below shows an LED + 300 ohm resistor on a breadboard. This is optional. The LED is not a necessary component of CH376S module communication.

Also be aware that the CH376S USB module has an onboard LED just above the TXD and GND pins near the USB port. This LED will only turn on providing the CH376S module is in USB mode AND a USB device has been inserted into the USB port. Both conditions must be met before the module's onboard LED will illuminate. You will not see the LED turn on just by powering the board.
 
The wire diagram below is the correct setup for Serial communication between an Arduino UNO and the CH376S module. If you wish to use SPI or Parallel communication, you will need to refer to the datasheet.


 
 

Arduino Sketch


 
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
/* ===============================================================
      Project: CH376S USB Read/Write Module testing ground
       Author: Scott C
      Created: 1st May 2015
  Arduino IDE: 1.6.2
      Website: http://arduinobasics.blogspot.com/p/arduino-basics-projects-page.html
  Description: This project will allow you to perform many of the functions available on the CH376S module.
               Checking connection to the module, putting the module into USB mode, resetting the module, 
               reading, writing, appending text to files on the USB stick. This is very useful alternative to
               SD card modules, plus it doesn't need any libraries.
================================================================== */

#include <SoftwareSerial.h>

byte computerByte;           //used to store data coming from the computer
byte USB_Byte;               //used to store data coming from the USB stick
int LED = 13;                //the LED is connected to digital pin 13 
int timeOut = 2000;          //TimeOut is 2 seconds. This is the amount of time you wish to wait for a response from the CH376S module.
String wrData = "What is the meaning of life ?";     //We will write this data to a newly created file.
String wrData2 = "42";                                   //We will append this data to an already existing file.

SoftwareSerial USB(10, 11);                           // Digital pin 10 on Arduino (RX) connects to TXD on the CH376S module
                                                      // Digital pin 11 on Arduino (TX) connects to RXD on the CH376S module
                                                      // GND on Arduino to GND on CH376S module
                                                      // 5V on Arduino to 5V on CH376S module
//==============================================================================================================================================
void setup() {
  Serial.begin(9600);                                 // Setup serial communication with the computer (using a baud rate of 9600 on serial monitor)
  USB.begin(9600);                                    // Setup serial communication with the CH376S module (using the default baud rate of 9600)
  pinMode(LED,OUTPUT);                                // Define digital pin 13 as an OUTPUT pin - so that we can use it with an LED
  digitalWrite(LED,LOW);                              // Turn off the LED
}

//================================================================================================================================================
void loop() {
  if(Serial.available()){
    computerByte = Serial.read();                      //read any incoming bytes from the Serial monitor, and store this byte in the variable called computerByte
    if(computerByte==49){               //1            //If you send the number 1 from the serial monitor, the arduino will read it as digital number 49. Google "ascii table" for more info.
      printCommandHeader("COMMAND1: CHECK CONNECTION");
      checkConnection(0x01);                           // Check for successful connection and communication with the CH376S module.
    } 
    if(computerByte==50){               //2
     printCommandHeader("COMMAND2: set_USB_Mode");
      set_USB_Mode(0x06);                              // Code used to enable read/write communication and monitoring of the USB stick
    }
    if(computerByte==51){               //3
      printCommandHeader("COMMAND3: resetALL");
      resetALL();                                      // Reset the USB device
    }
    if(computerByte==52){               //4
      printCommandHeader("COMMAND4: Create and Write to File : TEST4.TXT");
      writeFile("TEST4.TXT", wrData);                  // Create a file called TEST4.TXT and then Write the contents of wrData to this file
    }
    if(computerByte==53){               //5
      printCommandHeader("COMMAND5: Read File: TEST4.TXT");
      readFile("TEST4.TXT");                           // Read the contents of this file on the USB disk, and display contents in the Serial Monitor
    }
    if(computerByte==54){               //6
      printCommandHeader("COMMAND6: Append data to file: TEST4.TXT");
      appendFile("TEST4.TXT", wrData2);                // Append data to the end of the file.
    }
    if(computerByte==55){               //7
      printCommandHeader("COMMAND7: Delete File: TEST4.TXT");
      fileDelete("TEST4.TXT");                         // Delete the file named TEST4.TXT
    }
    if(computerByte==56){               //8
      printCommandHeader("COMMAND8: Read File: TEST2.TXT");
      readFile("TEST2.TXT");                           // Read the contents of the TEST2.TXT file on the USB disk, and display contents in the Serial Monitor
    }
    if(computerByte==57){               //9
      printCommandHeader("COMMAND9: Read File: TEST3.TXT");
      readFile("TEST3.TXT");                           // Read the contents of the TEST3.TXT file on the USB disk, and display contents in the Serial Monitor
    }
  }
  
  if(USB.available()){                                 // This is here to capture any unexpected data transmitted by the CH376S module
    Serial.print("CH376S has just sent this code:");
    Serial.println(USB.read(), HEX);
  }
}

//END OF LOOP FUNCTION ========================================================================================================================================

//print Command header
void printCommandHeader(String header){
   Serial.println("======================");
   Serial.println("");
   Serial.println(header);
   Serial.println("----------------------");
}

//checkConnection==================================================================================
//This function is used to check for successful communication with the CH376S module. This is not dependant of the presence of a USB stick.
//Send any value between 0 to 255, and the CH376S module will return a number = 255 - value. 
void checkConnection(byte value){
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x06);
  USB.write(value);
  
  if(waitForResponse("checking connection")){       //wait for a response from the CH376S. If CH376S responds, it will be true. If it times out, it will be false.
    if(getResponseFromUSB()==(255-value)){
       Serial.println(">Connection to CH376S was successful.");
       blinkLED();                               //blink the LED for 1 second if the connection was successful
    } else {
      Serial.print(">Connection to CH376S - FAILED.");
    }
  }
}

//set_USB_Mode=====================================================================================
//Make sure that the USB is inserted when using 0x06 as the value in this specific code sequence
void set_USB_Mode (byte value){
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x15);
  USB.write(value);
  
  delay(20);
  
  if(USB.available()){
    USB_Byte=USB.read();
    //Check to see if the command has been successfully transmitted and acknowledged.
    if(USB_Byte==0x51){                                   // If true - the CH376S has acknowledged the command.
        Serial.println("set_USB_Mode command acknowledged"); //The CH376S will now check and monitor the USB port
        USB_Byte = USB.read();
        
        //Check to see if the USB stick is connected or not.
        if(USB_Byte==0x15){                               // If true - there is a USB stick connected
          Serial.println("USB is present");
          blinkLED();                                     // If the process was successful, then turn the LED on for 1 second 
        } else {
          Serial.print("USB Not present. Error code:");   // If the USB is not connected - it should return an Error code = FFH
          Serial.print(USB_Byte, HEX);
          Serial.println("H");
        }
        
    } else {
        Serial.print("CH3765 error!   Error code:");
        Serial.print(USB_Byte, HEX);
        Serial.println("H");
    }   
  }
  delay(20);
}

//resetALL=========================================================================================
//This will perform a hardware reset of the CH376S module - which usually takes about 35 msecs =====
void resetALL(){
    USB.write(0x57);
    USB.write(0xAB);
    USB.write(0x05);
    Serial.println("The CH376S module has been reset !");
    delay(200);
}

//readFile=====================================================================================
//This will send a series of commands to read data from a specific file (defined by fileName)
void readFile(String fileName){
  resetALL();                     //Reset the module
  set_USB_Mode(0x06);             //Set to USB Mode
  diskConnectionStatus();         //Check that communication with the USB device is possible
  USBdiskMount();                 //Prepare the USB for reading/writing - you need to mount the USB disk for proper read/write operations.
  setFileName(fileName);          //Set File name
  fileOpen();                     //Open the file for reading
  int fs = getFileSize();         //Get the size of the file
  fileRead();                     //***** Send the command to read the file ***
  fileClose(0x00);                //Close the file
}

//writeFile========================================================================================
//is used to create a new file and then write data to that file. "fileName" is a variable used to hold the name of the file (e.g TEST.TXT). "data" should not be greater than 255 bytes long. 
void writeFile(String fileName, String data){
  resetALL();                     //Reset the module
  set_USB_Mode(0x06);             //Set to USB Mode
  diskConnectionStatus();         //Check that communication with the USB device is possible
  USBdiskMount();                 //Prepare the USB for reading/writing - you need to mount the USB disk for proper read/write operations.
  setFileName(fileName);          //Set File name
  if(fileCreate()){               //Try to create a new file. If file creation is successful
    fileWrite(data);              //write data to the file.
  } else {
    Serial.println("File could not be created, or it already exists");
  }
  fileClose(0x01);
}

//appendFile()====================================================================================
//is used to write data to the end of the file, without erasing the contents of the file.
void appendFile(String fileName, String data){
    resetALL();                     //Reset the module
    set_USB_Mode(0x06);             //Set to USB Mode
    diskConnectionStatus();         //Check that communication with the USB device is possible
    USBdiskMount();                 //Prepare the USB for reading/writing - you need to mount the USB disk for proper read/write operations.
    setFileName(fileName);          //Set File name
    fileOpen();                     //Open the file
    filePointer(false);             //filePointer(false) is to set the pointer at the end of the file.  filePointer(true) will set the pointer to the beginning.
    fileWrite(data);                //Write data to the end of the file
    fileClose(0x01);                //Close the file using 0x01 - which means to update the size of the file on close. 
}
  
//setFileName======================================================================================
//This sets the name of the file to work with
void setFileName(String fileName){
  Serial.print("Setting filename to:");
  Serial.println(fileName);
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x2F);
  USB.write(0x2F);         // Every filename must have this byte to indicate the start of the file name.
  USB.print(fileName);     // "fileName" is a variable that holds the name of the file.  eg. TEST.TXT
  USB.write((byte)0x00);   // you need to cast as a byte - otherwise it will not compile.  The null byte indicates the end of the file name.
  delay(20);
}

//diskConnectionStatus================================================================================
//Check the disk connection status
void diskConnectionStatus(){
  Serial.println("Checking USB disk connection status");
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x30);

  if(waitForResponse("Connecting to USB disk")){       //wait for a response from the CH376S. If CH376S responds, it will be true. If it times out, it will be false.
    if(getResponseFromUSB()==0x14){               //CH376S will send 0x14 if this command was successful
       Serial.println(">Connection to USB OK");
    } else {
      Serial.print(">Connection to USB - FAILED.");
    }
  }
}

//USBdiskMount========================================================================================
//initialise the USB disk and check that it is ready - this process is required if you want to find the manufacturing information of the USB disk
void USBdiskMount(){
  Serial.println("Mounting USB disk");
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x31);

  if(waitForResponse("mounting USB disk")){       //wait for a response from the CH376S. If CH376S responds, it will be true. If it times out, it will be false.
    if(getResponseFromUSB()==0x14){               //CH376S will send 0x14 if this command was successful
       Serial.println(">USB Mounted - OK");
    } else {
      Serial.print(">Failed to Mount USB disk.");
    }
  }
}

//fileOpen========================================================================================
//opens the file for reading or writing
void fileOpen(){
  Serial.println("Opening file.");
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x32);
  if(waitForResponse("file Open")){                 //wait for a response from the CH376S. If CH376S responds, it will be true. If it times out, it will be false.
    if(getResponseFromUSB()==0x14){                 //CH376S will send 0x14 if this command was successful  
       Serial.println(">File opened successfully.");
    } else {
      Serial.print(">Failed to open file.");
    }
  }
}

//setByteRead=====================================================================================
//This function is required if you want to read data from the file. 
boolean setByteRead(byte numBytes){
  boolean bytesToRead=false;
  int timeCounter = 0;
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x3A);
  USB.write((byte)numBytes);   //tells the CH376S how many bytes to read at a time
  USB.write((byte)0x00);
  if(waitForResponse("setByteRead")){       //wait for a response from the CH376S. If CH376S responds, it will be true. If it times out, it will be false.
    if(getResponseFromUSB()==0x1D){         //read the CH376S message. If equal to 0x1D, data is present, so return true. Will return 0x14 if no data is present.
      bytesToRead=true;
    }
  }
  return(bytesToRead);
} 

//getFileSize()===================================================================================
//writes the file size to the serial Monitor.
int getFileSize(){
  int fileSize=0;
  Serial.println("Getting File Size");
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x0C);
  USB.write(0x68);
  delay(100);
  Serial.print("FileSize =");
  if(USB.available()){
    fileSize = fileSize + USB.read();
  } 
  if(USB.available()){
    fileSize = fileSize + (USB.read()*255);
  } 
  if(USB.available()){
    fileSize = fileSize + (USB.read()*255*255);
  } 
  if(USB.available()){
    fileSize = fileSize + (USB.read()*255*255*255);
  }     
  Serial.println(fileSize);
  delay(10);
  return(fileSize);
}


//fileRead========================================================================================
//read the contents of the file
void fileRead(){
  Serial.println("Reading file:");
  byte firstByte = 0x00;                     //Variable to hold the firstByte from every transmission.  Can be used as a checkSum if required.
  byte numBytes = 0x40;                      //The maximum value is 0x40  =  64 bytes
 
  while(setByteRead(numBytes)){              //This tells the CH376S module how many bytes to read on the next reading step. In this example, we will read 0x10 bytes at a time. Returns true if there are bytes to read, false if there are no more bytes to read.
    USB.write(0x57);
    USB.write(0xAB);
    USB.write(0x27);                          //Command to read ALL of the bytes (allocated by setByteRead(x))
    if(waitForResponse("reading data")){      //Wait for the CH376S module to return data. TimeOut will return false. If data is being transmitted, it will return true.
        firstByte=USB.read();                 //Read the first byte
        while(USB.available()){
          Serial.write(USB.read());           //Send the data from the USB disk to the Serial monitor
          delay(1);                           //This delay is necessary for successful Serial transmission
        }
    }
    if(!continueRead()){                       //prepares the module for further reading. If false, stop reading.
      break;                                   //You need the continueRead() method if the data to be read from the USB device is greater than numBytes.
    }
  }
  Serial.println();
  Serial.println("NO MORE DATA");
}

//fileWrite=======================================================================================
//are the commands used to write to the file
void fileWrite(String data){
  Serial.println("Writing to file:");
  byte dataLength = (byte) data.length();         // This variable holds the length of the data to be written (in bytes)
  Serial.println(data);
  Serial.print("Data Length:");
  Serial.println(dataLength);
  delay(100);
  // This set of commands tells the CH376S module how many bytes to expect from the Arduino.  (defined by the "dataLength" variable)
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x3C);
  USB.write((byte) dataLength);
  USB.write((byte) 0x00);
  if(waitForResponse("setting data Length")){      // Wait for an acknowledgement from the CH376S module before trying to send data to it
    if(getResponseFromUSB()==0x1E){                // 0x1E indicates that the USB device is in write mode.
      USB.write(0x57);
      USB.write(0xAB);
      USB.write(0x2D);
      USB.print(data);                             // write the data to the file
  
      if(waitForResponse("writing data to file")){   // wait for an acknowledgement from the CH376S module
      }
      Serial.print("Write code (normally FF and 14): ");
      Serial.print(USB.read(),HEX);                // code is normally 0xFF
      Serial.print(",");
      USB.write(0x57);
      USB.write(0xAB);
      USB.write(0x3D);                             // This is used to update the file size. Not sure if this is necessary for successful writing.
      if(waitForResponse("updating file size")){   // wait for an acknowledgement from the CH376S module
      }
      Serial.println(USB.read(),HEX);              //code is normally 0x14
    }
  }
}

//continueRead()==================================================================================
//continue to read the file : I could not get this function to work as intended.
boolean continueRead(){
  boolean readAgain = false;
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x3B);
  if(waitForResponse("continueRead")){       //wait for a response from the CH376S. If CH376S responds, it will be true. If it times out, it will be false.
     if(getResponseFromUSB()==0x14){         //CH376S will send 0x14 if this command was successful
       readAgain=true;
     }
  }
  return(readAgain);
} 

//fileCreate()========================================================================================
//the command sequence to create a file
boolean fileCreate(){
  boolean createdFile = false;
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x34);
  if(waitForResponse("creating file")){       //wait for a response from the CH376S. If file has been created successfully, it will return true.
     if(getResponseFromUSB()==0x14){          //CH376S will send 0x14 if this command was successful
       createdFile=true;
     }
  }
  return(createdFile);
}


//fileDelete()========================================================================================
//the command sequence to delete a file
void fileDelete(String fileName){
  setFileName(fileName);
  delay(20);
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x35);
  if(waitForResponse("deleting file")){       //wait for a response from the CH376S. If file has been created successfully, it will return true.
     if(getResponseFromUSB()==0x14){          //CH376S will send 0x14 if this command was successful
       Serial.println("Successfully deleted file");
     }
  }
}
  

//filePointer========================================================================================
//is used to set the file pointer position. true for beginning of file, false for the end of the file.
void filePointer(boolean fileBeginning){
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x39);
  if(fileBeginning){
    USB.write((byte)0x00);             //beginning of file
    USB.write((byte)0x00);
    USB.write((byte)0x00);
    USB.write((byte)0x00);
  } else {
    USB.write((byte)0xFF);             //end of file
    USB.write((byte)0xFF);
    USB.write((byte)0xFF);
    USB.write((byte)0xFF);
  }
  if(waitForResponse("setting file pointer")){       //wait for a response from the CH376S. 
     if(getResponseFromUSB()==0x14){                 //CH376S will send 0x14 if this command was successful
       Serial.println("Pointer successfully applied");
     }
  }
}


//fileClose=======================================================================================
//closes the file
void fileClose(byte closeCmd){
  Serial.println("Closing file:");
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x36);
  USB.write((byte)closeCmd);                                // closeCmd = 0x00 = close without updating file Size, 0x01 = close and update file Size

  if(waitForResponse("closing file")){                      // wait for a response from the CH376S. 
     byte resp = getResponseFromUSB();
     if(resp==0x14){                                        // CH376S will send 0x14 if this command was successful
       Serial.println(">File closed successfully.");
     } else {
       Serial.print(">Failed to close file. Error code:");
       Serial.println(resp, HEX);
     }  
  }
}

//waitForResponse===================================================================================
//is used to wait for a response from USB. Returns true when bytes become available, false if it times out.
boolean waitForResponse(String errorMsg){
  boolean bytesAvailable = true;
  int counter=0;
  while(!USB.available()){     //wait for CH376S to verify command
    delay(1);
    counter++;
    if(counter>timeOut){
      Serial.print("TimeOut waiting for response: Error while: ");
      Serial.println(errorMsg);
      bytesAvailable = false;
      break;
    }
  }
  delay(1);
  return(bytesAvailable);
}

//getResponseFromUSB================================================================================
//is used to get any error codes or messages from the CH376S module (in response to certain commands)
byte getResponseFromUSB(){
  byte response = byte(0x00);
  if (USB.available()){
    response = USB.read();
  }
  return(response);
}



//blinkLED==========================================================================================
//Turn an LED on for 1 second
void blinkLED(){
  digitalWrite(LED, HIGH);
  delay(1000);
  digitalWrite(LED,LOW);
}


If you copy and paste this code directly into the Arduino IDE; you may get a warning like this when you compile the code:
 
   "Low memory available, stability problems may occur".
 
I managed to run the sketch without any issues, however, I did experience problems with some of the methods when I had made further memory hungry modifications. If you do encounter problems, I would recommend that you eliminate any methods which you do not plan to use, and perhaps reduce the number of Serial.print statements throughout the code. However, please note that some of the methods will not work unless the module is in the correct state, so be careful which methods you delete. For example, I found that I could get some simple functionality without the "USBdiskMount()" method. However, I could not read/write data beyond a certain length without this method.
 
Also please note, that some of the methods called within the reading and writing sequence do not need to be called every time. They can be called once in setup, while other methods within the sequence will need to be called every time. I grouped them all together for simplicity.


Serial Commands

Have a look at the following presentation for a summary of the Serial commands used in this tutorial:
 


 
 



If you like this page, please do me a favour and show your appreciation :

 
Visit my ArduinoBasics Google + page.
Follow me on Twitter by looking for ScottC @ArduinoBasics.
I can also be found on Pinterest and Instagram.
Have a look at my videos on my YouTube channel.




However, if you do not have a google profile...
Feel free to share this page with your friends in any way you see fit.