Mutila: Mouse's Utilities for Arduino
Oft-used utilities: debouncing buttons, averaging samples, and so on.
DFPlayerMini.cpp
1 #include <string.h>
2 #include <DFPlayerMini.h>
3 #include <MutilaDebug.h>
4 #include "Millis.h"
5 
6 DFPlayerMini::DFPlayerMini(Stream& serial, const uint8_t busyPin) :
7  _serial(serial),
8  _busyPin(busyPin),
9  _lastCmdSent(0)
10 {
11 }
12 
13 void DFPlayerMini::begin(bool bootWait)
14 {
15  pinMode(_busyPin, INPUT_PULLUP);
16  resetSendBuf();
17  if (bootWait) {
18  delay(600);
19  }
20 }
21 
22 void DFPlayerMini::resetSendBuf()
23 {
24  // Set up the send buffer with sentinels and such
25  memset(&_sendBuf, 0, sizeof(uint8_t) * BufferLength);
26  _sendBuf[0] = 0x7E; // I assume these are like magic bytes to
27  _sendBuf[1] = 0xFF; // idenfity this as a valid buffer?
28  _sendBuf[2] = 0x06; // payload length?
29  _sendBuf[9] = 0xEF; // send mark?
30 }
31 
33 {
34  _sendBuf[PacketOffsetCmd] = (uint8_t)cmd;
35  copyBigend(_sendBuf+PacketOffsetArg, arg);
36  fillChecksum();
37  _serial.flush();
38  serialCmd();
39 }
40 
41 // This function is for debugging the content of the
42 // receive buffer, and is only used when DEBUG is set
43 void dumpBuf(uint8_t* buf, uint8_t ptr, bool ln=true)
44 {
45  for (uint8_t i=0; i<DFPlayerMini::BufferLength; i++) {
46  _DB(F(" 0x"));
47  _DB(buf[i], HEX);
48  }
49  _DB(F(" ptr="));
50  _DB(ptr);
51  if (ln) {
52  _DBLN(' ');
53  }
54 }
55 
57 {
58  for (uint8_t i=1; i<=tries; i++) {
59  _DB(F("DFPlayerMini::query try "));
60  _DB(i);
61  _DB(F(" of "));
62  _DBLN(tries);
63  DFPResponse r = _query(cmd);
64  if (r.status == DFPResponse::Ok) {
65  return r;
66  }
67  // Add a random delay in case of sync problems
68  delay(random(5));
69  }
70  _DB(F("DFPlayerMini::query FAILED all "));
71  _DB(tries);
72  _DBLN(F(" tries"));
73  return DFPResponse();
74 }
75 
76 DFPResponse DFPlayerMini::_query(DFPlayerMini::Cmd cmd)
77 {
78  // This will hold our result. Note: response.status is Incomplete
79  // after the response object has been constructed.
80  DFPResponse response;
81 
82  // A buffer for the reply data
83  uint8_t buf[BufferLength];
84  memset(buf, 0, BufferLength*sizeof(uint8_t));
85 
86  // A pointer into the buffer where we will write incoming bytes
87  uint8_t ptr = 0;
88 
89  uint32_t startSend = Millis();
90  sendCmd(cmd);
91  uint32_t startRecv = Millis();
92 
93  // Populate buffer with response.
94  while(true) {
95  // Handle timeouts
96  if (MillisSince(startRecv) > ResponseTimeoutMs) {
97  response.status = DFPResponse::Timeout;
98  break;
99  }
100 
101  // Add bytes to buffer
102  while (_serial.available() > 0 && ptr < BufferLength) {
103  int c = _serial.read();
104  if (c<0) {
105  response.status = DFPResponse::SerialError;
106  break;
107  }
108 
109  if (ptr == 0 && c != 0x7E) {
110  // skip until we see the start of a proper response
111  } else if (ptr == 1 && c != 0xFF) {
112  // not proper header, reset
113  ptr = 0;
114  buf[0] = 0;
115  } else {
116  // looking good - append to buffer
117  buf[ptr++] = (uint8_t)c;
118  }
119  }
120 
121  // We have a full buffer with the proper header - continue
122  // to the next step: validation of the message
123  if (ptr == BufferLength) {
124  break;
125  }
126  }
127  // calculate how long comms took
128  uint32_t durationRecv = MillisSince(startRecv);
129 
130  _DB(F("DF RX:"));
131  dumpBuf(buf, ptr, false);
132  _DB(F(" send="));
133  _DB(MillisSince(startRecv, startSend));
134  _DB(F("ms, recv="));
135  _DB(durationRecv);
136  _DB(F("ms "));
137 
138  // flush the serial buffers just in case of junk
139  _serial.flush();
140 
141  // put results into response object
142  response.messageType = buf[PacketOffsetCmd];
143  uint8_t* aptr = (uint8_t*)(&(response.arg));
144  aptr[0] = buf[PacketOffsetArg+1];
145  aptr[1] = buf[PacketOffsetArg];
146 
147  // Validate the packet
148  uint16_t cksum = calculateChecksum(buf);
149  // check the terminator looks OK
150  if (buf[0] != 0x7E || buf[1] != 0xFF || buf[9] != 0xEF) {
151  _DBLN(F("ERR: head/term"));
152  response.status = DFPResponse::Invalid;
153  } else if (*((uint8_t*)&cksum) != buf[PacketOffsetCkSum+1] || *(1+(uint8_t*)&cksum) != buf[PacketOffsetCkSum]) {
154  response.status = DFPResponse::Invalid;
155  _DBLN(F("ERR: cksum"));
156  } else {
157  // OK, we have a valid response
158  response.status = DFPResponse::Ok;
159  _DBLN(F("VALID"));
160  }
161  return response;
162 }
163 
165 {
166  if (_busyPin != 0) {
167  return !digitalRead(_busyPin);
168  } else {
170  return r.arg != 512; // Not certain about this, but it seems to work...
171  }
172 }
173 
174 void DFPlayerMini::copyBigend(uint8_t *dst, uint16_t value)
175 {
176  uint8_t* dp = (uint8_t*)dst;
177  uint8_t* vp = (uint8_t*)&value;
178  dp[0] = vp[1];
179  dp[1] = vp[0];
180 }
181 
182 void DFPlayerMini::fillChecksum()
183 {
184  uint16_t checksum = calculateChecksum(_sendBuf);
185  copyBigend(_sendBuf+PacketOffsetCkSum, checksum);
186 }
187 
188 uint16_t DFPlayerMini::calculateChecksum(uint8_t *thebuf) {
189  uint16_t sum = 0;
190  for (uint8_t i=1; i<PacketOffsetCkSum; i++) {
191  sum += thebuf[i];
192  }
193  return -sum;
194 }
195 
196 void DFPlayerMini::serialCmd()
197 {
198  // wait until some short time has passed since the last command
199  // so the DFPlayer Mini has had a chance to process it...
200  while (MillisSince(_lastCmdSent) < MinimumTimeMs) {
201  delay(1);
202  }
203  _DB(F("DF TX:"));
204  for (uint8_t i=0; i<BufferLength; i++) {
205  _DB(F(" 0x"));
206  _DB(_sendBuf[i],HEX);
207  }
208  _DBLN(' ');
209  _lastCmdSent = Millis();
210  for (uint8_t i=0; i<BufferLength; i++) {
211  _serial.write(_sendBuf[i]);
212  }
213 }
214 
216 {
218  return r.status == DFPResponse::Ok;
219 }
220 
221 
DFPlayerMini(Stream &serial, const uint8_t busyPin=0)
Definition: DFPlayerMini.cpp:6
Indicates broken serial comms.
Definition: DFPlayerMini.h:159
DFPResponse query(Cmd cmd, uint8_t tries=3)
Messaged received and is valid.
Definition: DFPlayerMini.h:157
void sendCmd(Cmd cmd, uint16_t arg=0)
Get some meaningless status code.
Definition: DFPlayerMini.h:59
Response was not validated (bad cksum, header etc)
Definition: DFPlayerMini.h:160
Message timed out (DFPlayerMini::ResponseTimeoutMs ms passed)
Definition: DFPlayerMini.h:158
void begin(bool bootWait=true)