#include "map.h"
#include <Adafruit_NeoPixel.h>
#include <HardwareSerial.h>
#include "RTClib.h"
#define countof(a) (sizeof(a) / sizeof(a[0]))
RobotPosition currentPosition; //结构体获取状态和位置
RTC_DS1307 rtc;
Adafruit_NeoPixel rgb_display = Adafruit_NeoPixel(WS2812_nums /*92*56*/, WS2812_DATA_PIN, NEO_GRB + NEO_KHZ800);
char daysOfTheWeek[7][12] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
bool registers[numOfRegisterPins]; //56
bool ws2812_reg[56]; //56条灯带的状态
int radom_num[24];
void setup() {
Serial.begin(115200);
Serial1.begin(115200, SERIAL_8N1, 25 /*RX*/, 26 /*TX*/); //控制步进电机的串口
//delay(5000);
//DateTime(__DATE__, __TIME__);
Serial.println(printDateTime(DateTime(__DATE__, __TIME__)));
Wire.begin(27 /*sda*/, 14 /*scl*/, 100000 /*频率*/);
if (!rtc.begin()) {
Serial.println("连接失败");
} else {
Serial.println("连接成功");
}
randomSeed(micros());
/*
if (!rtc.isrunning()) {
Serial.println("RTC is NOT running, let's set the time!");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
*/
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
pinMode(HC595_SHCP_PIN, OUTPUT); //串行时钟针脚配置成输出模式
pinMode(HC595_STCP_PIN, OUTPUT); //锁存针脚配置成输出模式
pinMode(HC595_DS_PIN, OUTPUT); //数据输入针脚配置成输出模式
rgb_display.begin(); //灯带初始化
// rgb_display.setBrightness(64); //灯带亮度
for (int i = 0; i < numOfRegisterPins; i++) { //初始化引脚为低电平
registers[i] = 0;
ws2812_reg[i] = 0;
}
write_ws2812_state(ws2812_reg);
writeRegisters(registers);
Serial1.println("G10 P0 L20 X0 Y0 Z0");
delay(1000);
//delay(1000);
// Serial1.println("G01 X100 Y100 Z100 F1000");
}
void loop() {
//test_show(); //灯光测试
//test_rtc();时钟测试
for (int i = 0; i < numOfRegisterPins; i++) { //初始化引脚为低电平
registers[i] = 1;
ws2812_reg[i] = 1;
write_ws2812_state(ws2812_reg);
writeRegisters(registers);
delay(200);
}
for (int i = 0; i < numOfRegisterPins; i++) { //初始化引脚为低电平
registers[i] = 0;
ws2812_reg[i] = 0;
write_ws2812_state(ws2812_reg);
writeRegisters(registers);
delay(200);
}
Serial.println("下一次");
}
/*
1.机械动态:装置每一分钟,从左向右波浪形运动一次。往前顶形成波浪形
2.灯光控制:随机械动态同步亮灯,16:00前,亮灯区域在左侧30条,根据当下时间随机亮对应数量的灯(例:14:00亮14个缝隙),
波浪走到哪灯亮到哪,在一分钟运动过程中亮起的灯保持不灭。16:00后,亮灯区域是右半侧,其余逻辑保持一致。
这个装置大概高85cm,长160cm
每个单元体850mm*25mm 大小,共计 56 个单元体
16点前 0-28个单元体波浪,16点后28-56单元体波浪
16点前在 0-800mm范围内移动(最大同时点亮16条灯带), 16点后在800-1600mm范围内移动(最大同时点亮24-16条灯带)
28.5mm对应一个单元体的位置
*/
void simple_time_0() {
static int m_count = 0;
int _light_num = 0; //匹配时间的亮灯数量
int _apex_pos = 0; //凸起最高点的位置
DateTime now = rtc.now(); //每200ms获取时间
static int _old_minute = now.minute(), _old_apex = -1;
//_light_num = now.hour(); //获取 时 对应亮的灯
_light_num = 5; //获取 时 对应亮的灯
Serial.println(printDateTime(now));
//_light_num = 3;
Serial1.println("?"); //每200ms发送获取位置指令
get_xyz();
//Serial.println(String(currentPosition.x) + " " + String(currentPosition.y) + " " + String(currentPosition.z));
if (currentPosition.z != 0) {
_apex_pos = currentPosition.z / 28.5; //由滑台当前位置除以间距 得到顶升点在哪个单元体附近
} else {
_apex_pos = 0;
}
if (now.minute() - _old_minute >= 2) { //每两分钟分钟 来回一次
_old_minute = now.minute();
Serial1.println("G1 X50 Y0 Z0 F1000");
Serial1.println("G1 X50 Y1600 Z1600 F3000");
Serial1.println("G1 X0 Y1600 Z1600 F1000");
Serial1.println("G1 X0 Y0 Z0 F3000");
for (int i = 0; i < numOfRegisterPins; i++) { //先熄灭全部的
registers[i] = 0;
ws2812_reg[i] = 0;
}
write_ws2812_state(ws2812_reg);
writeRegisters(registers);
if (_light_num >= 0 && _light_num < 16) {
generateUniqueRandomArray(radom_num, _light_num, 0, 28);
} else if (_light_num >= 16 && _light_num < 24) {
generateUniqueRandomArray(radom_num, _light_num, 29, 56);
}
}
//Serial.println(String(currentPosition.z)+" "+String(_apex_pos));
if (_light_num >= 0 && _light_num < 16) { //在0-16点时间段
if (_apex_pos != _old_apex) { //凸起点不同,灯光亮的位置不同
_old_apex = _apex_pos;
for (int i = 0; i < _light_num; i++) {
if (radom_num[i] == _old_apex) {
registers[_old_apex] = 1; //
ws2812_reg[_old_apex] = 1;
write_ws2812_state(ws2812_reg);
writeRegisters(registers);
break;
}
}
for (int i = 0; i < numOfRegisterPins; i++) {
Serial.print(registers[i]);
Serial.print(" ");
}
Serial.println(" ");
}
} else if (_light_num >= 16 && _light_num < 24) { //在16-24点时间段
if (_apex_pos != _old_apex) { //凸起点不同,灯光亮的位置不同
_old_apex = _apex_pos;
for (int i = 0; i < _light_num; i++) {
if (radom_num[i] == _old_apex) {
registers[_old_apex] = 1; //
ws2812_reg[_old_apex] = 1;
write_ws2812_state(ws2812_reg);
writeRegisters(registers);
break;
}
}
}
for (int i = 0; i < numOfRegisterPins; i++) {
Serial.print(registers[i]);
Serial.print(" ");
}
Serial.println(" ");
}
}
/*
生成随机不重复数字
*/
void generateUniqueRandomArray(int arr[], int size, int min, int max) {
for (int i = 0; i < size; i++) {
int num;
bool isDuplicate;
do {
num = random(min, max + 1);
isDuplicate = false;
for (int j = 0; j < i; j++) {
if (arr[j] == num) {
isDuplicate = true;
break;
}
}
} while (isDuplicate);
arr[i] = num;
}
}
void get_xyz() {
static bool getFrameHead = 0, getFrameEnd = 0;
char byt = 0;
int length = 0, end_length = 0;
static String str_data = "";
while (Serial1.available() && (getFrameHead == 0)) {
byt = Serial1.read();
if (byt == '<') { //如果找到帧头 就退出
getFrameHead = 1;
getFrameEnd = 0;
end_length = Serial1.available();
}
}
//查找帧尾\r\n
if (getFrameHead == 1 && getFrameEnd == 0) {
for (int sys0 = 2; sys0 <= end_length; sys0++) {
byt = Serial1.read();
if (byt == '>') {
getFrameEnd = 1;
sys0 = end_length;
} else {
str_data += byt;
}
}
}
//找到帧头和帧尾
if (getFrameHead == 1 && getFrameEnd == 1) {
getFrameHead = 0;
getFrameEnd = 0;
// Serial.println(str_data);
int _index = 0;
String str_temp[5];
for (int i = 0; i < str_data.length(); i++) {
char byt = str_data.charAt(i);
if (byt == '|') {
_index++;
} else {
str_temp[_index] += byt;
}
}
if (str_temp[0].equals("Idle")) {
currentPosition.status = 1;
} else if (str_temp[0].equals("Run")) {
currentPosition.status = 2;
} else if (str_temp[0].equals("Hold")) {
currentPosition.status = 3;
} else if (str_temp[0].equals("Alarm")) {
currentPosition.status = 4;
}
_index = 0;
String str_xyz[3];
for (int i = 5; i < str_temp[1].length(); i++) {
char byt = str_temp[1].charAt(i);
if (byt == ',') {
_index++;
} else {
str_xyz[_index] += byt;
}
}
currentPosition.x = str_xyz[0].toFloat();
currentPosition.y = str_xyz[1].toFloat();
currentPosition.z = str_xyz[2].toFloat();
str_data = "";
}
}
/*
时钟模块测试
*/
void test_rtc() {
DateTime now = rtc.now();
Serial.println(printDateTime(now));
delay(1000);
}
/*
灯光测试
*/
void test_show() {
for (int i = 0; i < numOfRegisterPins; i++) { //初始化引脚为低电平
registers[i] = 0;
}
writeRegisters(registers);
//uint32_t old_time = millis();
for (int i = 0; i < numOfRegisterPins; i++) { //初始化引脚为低电平
ws2812_reg[i] = 0;
}
write_ws2812_state(ws2812_reg); //158ms完成一次传输
delay(1000);
for (int i = 0; i < numOfRegisterPins; i++) { //初始化引脚为低电平
registers[i] = 1;
}
writeRegisters(registers);
for (int i = 0; i < numOfRegisterPins; i++) { //初始化引脚为低电平
ws2812_reg[i] = 1;
}
write_ws2812_state(ws2812_reg);
delay(1000);
}
/*
格式化输出
*/
String printDateTime(const DateTime& dt) {
char datestring[50];
snprintf_P(datestring,
countof(datestring),
PSTR("%04u/%02u/%02u %02u:%02u:%02u"),
dt.year(),
dt.month(),
dt.day(),
dt.hour(),
dt.minute(),
dt.second());
return String(datestring);
}
/*
56条灯带的具体实现
*/
void write_ws2812_state(bool* _state) {
for (int i = 0; i < 56; i++) {
if (_state[i]) {
for (int j = 0; j < 92; j++) {
rgb_display.setPixelColor(j + i * 92, rgb_display.ColorHSV(9276, 200, 80));
}
} else {
for (int j = 0; j < 92; j++) {
rgb_display.setPixelColor(j + i * 92, 0);
}
}
}
// uint32_t old_time = millis();
rgb_display.show();
//Serial.println(millis() - old_time);
}
/*
移位寄存器控制的具体实现
*/
void writeRegisters(bool* _state) {
uint64_t data = 0;
for (int i = 0; i < numOfRegisterPins; i++) {
data |= uint64_t((_state[i] ? 1 : 0)) << i;
}
digitalWrite(HC595_STCP_PIN, LOW); //拉低锁存针脚,为传输数据做准备
for (int i = 0; i < NUM_SHIFT_REGS; i++) {
byte byteToSend = (data >> (8 * i)) & 0xFF;
shiftOut(HC595_DS_PIN, HC595_SHCP_PIN, LSBFIRST, byteToSend);
}
digitalWrite(HC595_STCP_PIN, HIGH); //拉高锁存针脚,结束传输
}