Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
A
arduino
Manage
Activity
Members
Labels
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Model registry
Operate
Terraform modules
Analyze
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Deike, Benedikt
arduino
Commits
11e60d82
Commit
11e60d82
authored
Nov 14, 2021
by
bav6096
Browse files
Options
Downloads
Patches
Plain Diff
inital commit
parent
cef6674b
No related branches found
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
Arduino.ino
+424
-0
424 additions, 0 deletions
Arduino.ino
with
424 additions
and
0 deletions
Arduino.ino
0 → 100644
+
424
−
0
View file @
11e60d82
/*!
* @file Arduino.ino
*
* This Arduino sketch is designed for an Arduino Nano, with which two NeoPixel
* rings from Adafruit are connected. It is assumed that both rings have 16 LEDs
* each.
*
* This code uses Adafruit's Neopixel library, which is released under a GNU Lesser
* General Public License.
*
* Objective:
This sketch should make it possible to use the Arduino Nano to play animation on
the two connected NeoPixel rings. Since the memory of the Nano is very limited,
there is the functionality to load animations via the serial interface.
*
* Let the left NeoPixel ring be abbreviated to: lR
* Let the right NeoPixel ring be abbreviated to: rR
*
* Wiring:
Arduino pin 6 -> lR Data Input
Arduino pin 7 -> rR Data Input
Arduino pin 5V -> lR Power
Arduino pin 5V -> rR Power
Arduino pin GND -> lR Ground
Arduino pin GND -> rR Ground
*/
#include
<Adafruit_NeoPixel.h>
#define MSG_SIZE 50 // Expected size of a received message in bytes.
#define BUF_SIZE 16 // Maximum number of frames that can be stored in either frame
// buffer.
#define L_PIN 6 // Arduino pin that is connected to the left NeoPixel ring.
#define R_PIN 7 // Arduino pin that is connected to the right NeoPixel ring.
#define PIXELS 16 // Number of pixels on either NeoPixel ring.
/*
* To make the serial communication a little more robust, all serial requests are
* answered by the Nano. Furthermore, the Nano only receives 50-bit messages and
* only sends 8-bit messages. The serial buffer is also emptied before each written
* message in order to be able to react to the following incoming message.
*
* There are two types of messages, data messages and instruction messages. Data
* messages are used to convey the animation frames, while instruction messages are
* used to convey instructions, which the Nano then executes.
*/
// Variables used for communication.
uint8_t
msg
[
MSG_SIZE
];
// Holds a received message.
int
msg_size
;
// Determines how many bits of a message are received.
bool
rcvd_msg
;
// Determines if all 50 bits of a message are received.
bool
rcvg_animation
;
// Determines if an animation is currently been received.
int
msgs_to_rcv
;
// If an animation is received: Determines the number of
// (data) messages that still have to be received.
/*
* Every animation consists of frames. A frame describes the colors of all 16
* pixels on one NeoPixel ring. Since two NeoPixel rings are connected to the
* Arduino, each animation consists of at least two frames, one frame for the left
* ring and one frame for the right ring. To play an animation, two frames are
* displayed in parallel. The term frame ensemble or ensemble is used to refer to
* these two frames that play in parallel.
*/
// Variables used for animation storage.
int
values_per_frame
;
// Number of bytes that are needed to store an entire
// frame.
uint8_t
**
l_frame_buffer
;
// Stores all animation frames for the left
// NeoPixel ring.
uint8_t
**
r_frame_buffer
;
// Stores all animation frames for the right
// NeoPixel ring.
uint8_t
num_ensembles
;
// Number of frame ensembles in the animation.
uint8_t
num_iterations
;
// Number of times the animation is repeated.
unsigned
long
frame_time
;
// Delay in milliseconds between each displayed
// frame ensemble.
// Variables used for animation playback.
bool
animation_loaded
;
// Determine if an animation is loaded.
bool
playback
;
// Determine if animation should be played.
long
start_time
;
// Time at which each individual frame ensemble
// started to be displayed.
uint8_t
curr_ensemble
;
// Frame ensemble that is currently displayed.
uint8_t
curr_iteration
;
// Animation iteration that is currently played.
// Variables used for NeoPixel ring.
Adafruit_NeoPixel
l_pixels
(
PIXELS
,
L_PIN
,
NEO_GRB
+
NEO_KHZ800
);
Adafruit_NeoPixel
r_pixels
(
PIXELS
,
R_PIN
,
NEO_GRB
+
NEO_KHZ800
);
void
setup
()
{
msg_size
=
0
;
rcvd_msg
=
false
;
rcvg_animation
=
false
;
msgs_to_rcv
=
0
;
// Three color values (red, green, blue) must be saved for each pixel.
values_per_frame
=
PIXELS
*
3
;
animation_loaded
=
false
;
playback
=
false
;
start_time
=
0
;
curr_ensemble
=
0
;
curr_iteration
=
0
;
init_frame_buffers
();
l_pixels
.
begin
();
r_pixels
.
begin
();
Serial
.
begin
(
57600
);
}
/*!
@brief Allocates memory for the two frame buffers.
@note The two frame buffers each have the structure of a 2D array in which
16 frames are stored. Each frame consists of 48 bytes of color
information. A total of 1536 bytes are allocated in this function.
*/
void
init_frame_buffers
()
{
l_frame_buffer
=
(
uint8_t
**
)
malloc
(
sizeof
(
uint8_t
*
)
*
BUF_SIZE
);
r_frame_buffer
=
(
uint8_t
**
)
malloc
(
sizeof
(
uint8_t
*
)
*
BUF_SIZE
);
for
(
int
i
=
0
;
i
<
BUF_SIZE
;
i
++
)
{
l_frame_buffer
[
i
]
=
(
uint8_t
*
)
malloc
(
sizeof
(
uint8_t
)
*
values_per_frame
);
r_frame_buffer
[
i
]
=
(
uint8_t
*
)
malloc
(
sizeof
(
uint8_t
)
*
values_per_frame
);
}
}
/*!
@brief Sends a 8-bit message via the serial connection. The serial buffer
is emptied beforehand in order to react to following incoming messages.
@param msg 8-bit message from Arduino.
@note This function is used to respond to incomming messages.
*/
void
respond
(
uint8_t
msg
)
{
while
(
Serial
.
available
()
>
0
)
Serial
.
read
();
// Flush serial buffer.
Serial
.
write
(
msg
);
}
/*!
@brief This function sends the color information in the two frame buffers to the
NeoPixel rings. The frame ensembles are shown delayed depending on the
animation speed. In addition, the animation is repeated depending on the
desired iterations.
*/
void
play_animation
()
{
if
(
curr_ensemble
<
num_ensembles
)
{
if
(
millis
()
>
frame_time
+
start_time
)
{
for
(
int
i
=
0
;
i
<
PIXELS
;
i
++
)
{
int
pixel
=
i
*
3
;
uint8_t
r
;
uint8_t
g
;
uint8_t
b
;
r
=
l_frame_buffer
[
curr_ensemble
][
pixel
+
0
];
g
=
l_frame_buffer
[
curr_ensemble
][
pixel
+
1
];
b
=
l_frame_buffer
[
curr_ensemble
][
pixel
+
2
];
l_pixels
.
setPixelColor
(
i
,
l_pixels
.
Color
(
r
,
g
,
b
));
r
=
r_frame_buffer
[
curr_ensemble
][
pixel
+
0
];
g
=
r_frame_buffer
[
curr_ensemble
][
pixel
+
1
];
b
=
r_frame_buffer
[
curr_ensemble
][
pixel
+
2
];
r_pixels
.
setPixelColor
(
i
,
r_pixels
.
Color
(
r
,
g
,
b
));
}
l_pixels
.
show
();
r_pixels
.
show
();
start_time
=
millis
();
curr_ensemble
++
;
}
}
if
(
curr_ensemble
>=
num_ensembles
)
{
if
(
millis
()
>
frame_time
+
start_time
)
{
if
(
num_iterations
!=
0xFF
)
curr_iteration
++
;
// Animation is looped indefenitly.
if
(
curr_iteration
>=
num_iterations
)
{
reset_playback
();
respond
(
0x4F
);
// Animation successfully played!
}
else
curr_ensemble
=
0
;
}
}
}
/*!
@brief Executes the appropriate function depending on the instruction read
from a received instruction message.
*/
void
handle_instruction
()
{
switch
((
char
)
msg
[
1
])
{
case
'A'
:
load_animation
();
break
;
case
'B'
:
start_playback
();
break
;
case
'C'
:
pause_playback
();
break
;
case
'D'
:
resume_playback
();
break
;
case
'E'
:
reset_display
();
break
;
default:
respond
(
0xF2
);
// Instruction was not recognized.
break
;
}
}
/*!
@brief Breaks down an instruction message into its individual components.
*/
void
load_animation
()
{
if
(
msg
[
2
]
%
2
!=
0
)
respond
(
0xF7
);
// Unequal number of frames for the
// left and right NeoPixel ring.
else
if
(
msg
[
2
]
*
0.5
>
BUF_SIZE
)
respond
(
0xF0
);
// Animation has to many frame ensembles.
else
if
(
msg
[
2
]
*
0.5
==
0x00
)
respond
(
0xF8
);
// Animation has no frame ensemble.
else
if
(
msg
[
7
]
==
0x00
)
respond
(
0xF6
);
// Animation has no iteration.
else
{
reset_playback
();
num_ensembles
=
msg
[
2
]
*
0.5
;
frame_time
=
msg
[
3
];
frame_time
=
(
frame_time
<<
8
)
+
msg
[
4
];
frame_time
=
(
frame_time
<<
8
)
+
msg
[
5
];
frame_time
=
(
frame_time
<<
8
)
+
msg
[
6
];
num_iterations
=
msg
[
7
];
animation_loaded
=
false
;
rcvg_animation
=
true
;
msgs_to_rcv
=
msg
[
2
];
respond
(
0x0F
);
// Waiting for frames to be send.
}
}
/*!
@brief Starts animation playback.
*/
void
start_playback
()
{
if
(
animation_loaded
)
{
playback
=
true
;
start_time
=
millis
()
-
frame_time
;
curr_ensemble
=
0
;
respond
(
0x1F
);
// Animation playback has been started.
}
else
respond
(
0xF1
);
// No animation is loaded.
}
/*!
@brief Pauses animation playback
*/
void
pause_playback
()
{
if
(
animation_loaded
)
{
playback
=
false
;
respond
(
0x5F
);
// Animation playback has been paused.
}
else
respond
(
0xF1
);
// No animation is loaded.
}
/*!
@brief Resumes animation playback.
*/
void
resume_playback
()
{
if
(
animation_loaded
&&
!
playback
)
{
playback
=
true
;
start_time
=
millis
()
-
frame_time
;
respond
(
0x6F
);
// Animation playback has been resumed.
}
else
if
(
playback
)
respond
(
0xF5
);
// Animation is playing.
else
respond
(
0xF1
);
// No animation is loaded.
}
/*!
@brief Resets all animation playback parameters.
*/
void
reset_playback
()
{
playback
=
false
;
start_time
=
0
;
curr_ensemble
=
0
;
curr_iteration
=
0
;
}
/*!
@brief Clears both NeoPixel rings.
*/
void
reset_display
()
{
l_pixels
.
clear
();
r_pixels
.
clear
();
l_pixels
.
show
();
r_pixels
.
show
();
respond
(
0x7F
);
// Displays have been cleared.
}
/*!
@brief Loads receiving data messages into one of the frame buffers.
@note Each animation consists of an even number of frames. All even frames in an
animation are stored in the left frame buffer and all odd frames in the
right one.
*/
void
handle_data
()
{
if
(
rcvg_animation
&&
msgs_to_rcv
>
0
)
{
int
frame
=
0.5
*
(
2
*
num_ensembles
-
msgs_to_rcv
);
// Data type int always rounds
// off a floating point number.
for
(
int
i
=
0
;
i
<
values_per_frame
;
i
++
)
{
if
(
msgs_to_rcv
%
2
==
0
)
l_frame_buffer
[
frame
][
i
]
=
msg
[
i
+
1
];
else
r_frame_buffer
[
frame
][
i
]
=
msg
[
i
+
1
];
}
msgs_to_rcv
--
;
if
(
msgs_to_rcv
==
0
)
{
rcvg_animation
=
false
;
animation_loaded
=
true
;
respond
(
0x3F
);
// Last frame successfully received.
}
else
respond
(
0x2F
);
// Frame successfully received.
}
else
respond
(
0xF3
);
// No data message expected.
}
/*!
@brief Responds to wrong formated message.
*/
void
handle_wrong_format
()
{
respond
(
0xF4
);
// Message has wrong format.
}
/*!
@brief Loades a received message form the serial buffer into memmory.
@note Data messages as well as instruction messages always have a length
of 50 bits.
*/
void
read_serial
()
{
while
(
Serial
.
available
()
>
0
)
{
if
(
msg_size
<
MSG_SIZE
)
{
msg
[
msg_size
]
=
Serial
.
read
();
msg_size
++
;
}
if
(
msg_size
>=
MSG_SIZE
)
{
rcvd_msg
=
true
;
break
;
}
}
}
/*!
@brief First, checks if a message is received. If thats the case determines if the
message is a data or an instruction message and preecedes accordingly. If the
received message is neither one of the two, informs the communication partner.
Secondly, checks if a message is currently beeing send to the Nano. If thats
the case, writes the message to memory.
Thirdly, if an animation is loaded and if the animaiton should be played,
plays the loaded animation.
@note Data messages beginn and end with ascii encoded curling brackets.
Instruction messages beginn and end with ascii encoded square brackets.
*/
void
loop
()
{
if
(
rcvd_msg
)
{
char
startMarker
=
msg
[
0
];
char
endMarker
=
msg
[
MSG_SIZE
-
1
];
if
(
startMarker
==
'['
&&
endMarker
==
']'
)
handle_instruction
();
else
if
(
startMarker
==
'{'
&&
endMarker
==
'}'
)
handle_data
();
else
handle_wrong_format
();
rcvd_msg
=
false
;
msg_size
=
0
;
}
else
if
(
Serial
.
available
()
>
0
)
read_serial
();
else
if
(
playback
&&
animation_loaded
)
play_animation
();
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment