top of page
  • ZackW

Automatically Scored Cornhole - Scorecorn - Part 1

Updated: Aug 23, 2022

This is another Work-In-Progress post. I'm essentially documenting as I go, partly for myself, partly for this blog, and partly for reference later as we write the official report.


For school, we have to complete a year of "capstone". This is essentially just a project we work on to demonstrate our engineering abilities, and we have to write a professional report of the project. It's also expected to typically work with a small group of people, and to follow best practices for organization of the project.


For our project, we chose to automate the score keeping of the lawn game "Cornhole". If you haven't played before, it's a pretty simple game where you have two boards set some distance apart. The goal of the game is to throw bags (like bean bags, except they're corn bags lol) onto the board and through a hole in it's center. Wikipedia explains it here.



Getting the DWM1000 Modules Working

My first real task is to get the DWM1000 modules we're using for ranging, to work with some Arduinos. Of course, the first thing I did after receiving the development boards was to plug them directly into an Arduino Uno and turn them on. How was I supposed to know that they were 3.3v development boards only? They're formatted as Arduino shields! grumbles


Anyway, I think that killed two boards right off the bat. Upon realizing this, I ordered two more development boards that I hadn't noticed on the market before. These have microcontrollers built in, but I foolishly didn't think about integrating those into the Arduino environment beforehand. The new development boards are quite feature complete, but are meant to be used either with proprietary libraries or with their own IDE setup. Great. So, for now those new and great development boards are sitting uselessly while I try and resurrect the simpler boards that I can interface with an Arduino.


Connection Issues


The first thing to do is to get the device to start talking to the Arduino. The 3.3v <-> 5v level shifter makes this more difficult than it should be, and I've already ordered a different type of level shifter in hopes it will work better. The below image is an example of the issues I was having.


The MOSI signal at the DWM1000

I actually went and found where the SPI speed was set in the library source code, and have since dropped it down through 2MHz and then ultimately 200kHz. From 16MHz. This is especially unfortunate since it's not clear what the lowest SPI speed the DWM supports is, with the (two different) datasheets almost implying that there is a minimum clock period. At higher frequencies even the clock signal disappears.

What happens to the clock signal at high frequencies

Things are looking up slightly when running at 200kHz though, since the DWM1000 module starts to actually output on it's MISO pin. I really wish the Arduino library did a better job at verifying operation and handling error states, rather than just blindly assuming things are working and proceeding as if. At this point I would love to switch just using a different library and IDE, but the amount of closed-source stuff in the stacks suggested by other people is almost worse than this situation. My teammates are also not as experienced with other environments than Arduino, I think, so I'm trying to push through on this. It might be that we use the nordic MCU based modules later on.


Another thing that would be great to have in the datasheet among the register information, would be some basic ID or status registers we could check to make sure the communication is actually working. These do actually exist, but it's not clear what the data should actually be. If not reporting 0 or F, the Arduino reads "DECA - model: 1, version: 3, revision: 0". Which I can only assume is correct, and helpfully the comment in the C API source code is '0xDECA0130', which aligns with that. Weird that would be in the C header and not the datasheet!

At this point it's fairly likely that we have communication with the DWM1000 working. I am running the 'DW1000Ng::enableLedBlinking();' function in hopes of making the DWM1000 blink and LED, but it doesn't seem to want to play. I need to hook up another Arduino and another DWM1000 to see if it can receive. The actual ranging might be a whole 'nother challenge unfortunately, with things like clock synchronization affecting the accuracy greatly. We need the highest accuracy possible for our algorithm.


Trying to get GPIO

Something that helps with basic debugging, and would ultimately be useful in the project, is having the GPIO pins working on the DWM1000. There is no SPI register documentation I can find from Qorvo for the device, because of course there isn't. It might be buried in one of the application notes but I haven't found it yet. The best I can do is their C API, a download available from their DW1000 product webpage. Their C code does have a register map with basic descriptions of the registers in the form of the definition names and some comments. There IS a 'setGPIOMode()' function in the Arduino library, but it's super not obvious how to use it. The source code for it doesn't seem to do anything about the port direction, and it seems like you should have to set that.


Things Improve

After taking the best circuit and program setup, and copying it, I was finally able to get a functioning pair of devices. The first program I ran was just the basic send and receive examples. I was able to have one device broadcast a message and the other one receive it, yay! Next on the list is to test ranging functionality. Actually, the second device I set up was one of the sketchy ones I previously over voted. Neat that it still seems to work! Hopefully it doesn't hold any annoying surprises for later on.

This is a big step in the right direction! It's only part of the story though, and the next part is probably going to be the hardest. Data is all well and good, but we really need distance measurement from these devices. I loaded a few of the different ranging examples and tried them, none of them seem to just work. They do include a reset pin configuration option, something the other example didn't include. They also ask for a chip select pin, which defaults to "SS". These are additional variables and should be checked they are working as expected, so let's grab the oscilloscope and check them.

Reset working - I verified the 3.3v side was running as well

The reset signal seems to only be pulled low once, after a reset of the MCU itself.

Chip select seems to be working

It's a bit disconcerting to see the voltage spike well above 3.3v on the chip-select signal. That seems to only happen on the new level shifters I got, the ones that use a single transistor instead of an IC. Regardless, I don't see anything about the two signals that would break anything, and they seem to be working. The APS013 application note from decawave/qorvo explains an implementation of two-way ranging. The other new thing is the IRQ pin, something I don't know much about. The datasheet says the IRQ pin is for an interrupt service request, something I could see being used for TWR. I guess it would be worth checking what that signal was doing. On the Uno, the board configured as the "initiator", the IRQ pin seems to trigger often.


This signal wasn't originally attached through the level shifter (why attach what you don't understand?), but upon seeing this I hooked it up. Hooking that up on the Initiator didn't have an effect on what was being printed on the serial monitor. On the "responder", the signal is also triggering often, in sort of a weird pattern which seems to trigger SPI transactions. That's a good sign, I think. This is with the 'TwoWayRangingInitiator' and 'TwoWayRangingResponder' examples. In checking the other examples,


TWR Struggles & Research

I finally sat down to read through the application notes to try and fine clues on debugging the TWR examples. I've found some gems already, like an easy way to write and read-back a large page of data to verify that SPI is working correctly. I've also found a hint about getting the TX/RX pins working; getting those two signals is top priority for debugging at this point. APS022 specifically mentions enabling "EXTRXE" and "EXTTXE", for "external receive enable" and "external transmit enable". The recommended API function is dwt_setlnapamode(1,1).


APS022 also talks about system status registers, something I've been wanting to check but been unable to. I'll have to check if the Arduino library gives easy access to these, or not. It also has a good graphic of power consumption on pg 15, which might be useful for estimating battery life.


APS013 (mentioned earlier) has some great details on the protocol itself. For a basic setup, a tag will announce itself using "Blink" messages and wait for a Ranging Init message from an anchor. Once past that discovery phase, the sends a "Poll" message to the anchor, which confirms it with a "Response". Then, the tag sends a "Final" message to finish up. Apparently there is an optional message back from the anchor to the tag, for potentially communicating the range to the tag if it needs to know it.

My guess (before reading more), would be the time-of-arrival data would be included in the variable # octets bit of the frame. The very next graphic (Figure 5) describes the message content for each packet. Reading onward, it's interesting to note the LSB in the time signature represents 15.65ps, which is about 0.185" when traveling at the speed of light. Application note APS011 goes into sources of error later on, and probably explains why we can't be that accurate.


Quick note: SPI speed of the SAMD21 on the Seeeduino XIAOs is ~12MHz max I believe, that's listed as a "typical" speed anyway. A couple forum posts suggest 12MHz is recomended, too. The chip on the Arduino Mega might be a lot slower, but power consumption on those might be less important. I hope it's okay if the tag and anchor can have different SPI speeds.


Back to Troubleshooting

The first thing to try is moving the radios apart from each other by a couple feet. I read somewhere about them not working correctly when placed next to each other, I think. Bit archaic, but worth trying.


After trying that random shot in the dark, I went about trying to get some more debug information from the device. I decided to attack the GPIO problem again, and used a really brutal approach to do it. I found the relevant code (dwt_setlnapamode) in the deca_device.c file supplied by Qorvo, and basically pieced together along with the existing arduino library code for the DW1000.

Note the heavy usage of magic numbers... I do feel a bit more confident about the GPIO system after doing that, it's some kind of weird mask based configuration system that is obtuse, but probably not too difficult to understand when I get there. I'll probably end up writing a pinMode() type adapter for it later.


Anyway, success! The EXTRXE and EXTTXE pins aren't actually the same ones as the LED pins (why??) but I can probe them with an oscilloscope. I checked that the tag (remember, it's responsible for iniating via a "Poll" packet) was toggling it's EXTTXE. Which it was. I then flipped the code around and made that device the anchor, to check if it was toggling it's receive pin. Which it was.

So, at least on that particular device, everything seems all hunky-dory yankee doodle. Next thing to do is read those two pins on the other device (happens to be the Uno).

... And they seem to be in working order. Now, if one is actually transmitting, and the other is actually receiving, where is our data?? This is kind of confusing, especially when turning those same pins on for the simple (working) message example that doesn't involve ranging. The behavior is the same; this implies the ranging should be working correctly as well.

The next thing that occurs to me besides just plainly not activating the transmitter or receiver, is that our SPI protocol is just too slow to properly communicate with the device while it's trying to do it's time-sensitive ranging. I tried to test this real quick by turning up the SPI on the Arduino's till they stopped communicating. 7MHz seemed to be about the limit (what?? thought that didn't work before).

Oscilloscope says it's running at about 8MHz. This USB oscilloscope (a diligent analog discovery 2) isn't fast, and struggles getting enough samples that fast. Well, the SPI theory is probably busted. I have a hard time believing that it doesn't work at 8MHz SPI. That leaves a hardware or software fault of some kind. It's probably worth setting up a device that hasn't been over-volted and seeing if it works. That would suck if it was just that. Investigating the Arduino library more might also be a good idea to see if anyone has similar issues.


I started digging through the github repo and found this issue referring to almost exactly the same problem. This is interesting because it's claimed the Arduino Uno can't supply enough current, which might explain this? Interesting that the messaging would work but not ranging. If I check the 3.3v rail for the Uno, I get this result:

Pretty big dip, there! The datasheet lists the minimum voltage as 2.8v. Lovely. I'm sure the Nano can't do much better, but at least while it was receiving the minimum voltage was 3.3v. Aight. Let's fix that. The Uno is actually supplying the 3.3v via a power supply built onto the DWS1000 shield, so the 5v rail is actually probably dipping and threatening the Arduino's functionality as well. Actually the 5v rail looked fine, weirdly enough. And, what's more is I couldn't get a reading that low on the Uno setup, again. The rest of the ones I took were above 2.8v. I even tried switching over to the TWR protocol and tried measuring that voltage dip, still fine. So, surely that can't be the issue?


This issue is also the same as what I'm doing, and claims pulling the IRQ pin low fixes the issue since there must be noise there. Well, technically this library supports a no-interrupts mode (which, incidentally, the messaging example uses). If I swap a few things around to theoretically make the ranging example work without the IRQ, it doesn't seem to change anything. Okay, well let's try adding that resistor then. I used nice and stiff 1k resistors. Nada, no change.


At this point I'm a bit lost on what to do. It would be good to start with a working, known, set up and expand from there. Wait. Ugh. I remember something from a long time ago, when setting up pin interrupts, I don't think all pins work for interrupts on the Arduino. If the library is using interrupts internally on the Arduino's, and only a specific pin works...

$&*)$#! Why is it always the last thing you try! And usually the dumbest! This whole time I've been fighting my own laziness, I set the IRQ pin read by the Arduino to digital pin 8. It defaults to pin 2. Guess what? Digital pin 2, is one of the two available default interrupt pins on an ATmega328p based Arduino...

Ah well. At least it works. There seems to be about a 0.05m variance in the reading, after a bit of averaging. Then, the signal will jump to a different value (10-20cm away) and hover around that in about the same variance. Not entirely sure what that's about, but if we can get even a 0.1m variance constantly, it should work for what we're doing.


This is a good time to end Part 1 of this Saga. And it really is going to be a Saga. It's taken me probably upwards past 30hrs to get this one thing working, and we have about 36 days until the next school term starts and we need to focus on report writing and data gathering. So, about 36 days to complete all the hard stuff!


What I Learned

Always check voltage specifications of new products you might be using! Attaching the 3.3v devices to 5v doesn't seem to have hurt them much, but it's quite a dumb way to ruin hardware. I also learned that if I ever write a library for a device (or, rather, make one public) then it really needs to have better error checking and error handling than the DW1000 Arduino library has. It was re-affirmed once again that it's often good to stop working on the hardware and read the application notes and source code. You can then come back to the hardware and made better educated decisions as to what might be wrong. It's also a great idea, as I learned, to look up similar builds that other people have done, and read about the lessons they learned. And, maybe the biggest lesson, is to check your wiring! Go over each connection and figure out exactly what it does and if it needs additional functionality like a pull-up/down, or like if it has to be attached to an interrupt handler inside the MCU. If someone specify a connection to a specific pin, maybe there's a reason it's that pin.

106 views0 comments

Recent Posts

See All
bottom of page