There's no such thing as bullet proof code. It's a mythical legend, a unicorn. We can (and should!) certainly try though. The first step to doing that is writing extremely legible and understandable code. You've done a pretty good job of that, but there's some room for improvement.

#Readability
##Use of Whitespace and Comments

It seems like a silly thing to pick on, but it really does matter. Consider this snippet from your code. It does some things right, but it *looks* messy.

>     For g = 1 To 10000
>         If Not IsEmpty(Cells(4 + g, 4).value) Then
>             NumberOfTags = NumberOfTags + 1
>         End If
>     Next g
> 
>     If Not ThisWorkbook.OPCServer1 Is Nothing Then       'safety
>          Exit Sub
> 
>     End If
> 
>     Set ThisWorkbook.OPCServer1 = New OPCServer
>     Call ThisWorkbook.OPCServer1.Connect(Cells(4, 2))                'connect to the OPC Server
>     Set OPCGroup1 = ThisWorkbook.OPCServer1.OPCGroups.Add(GrpName) 'add the group
> 
>     'add the 6 items
>     ReDim MyOPCItems(NumberOfTags)
> 
> 
>     For i = 1 To NumberOfTags
>         On Error GoTo Error_TagNotFound
>         If Cells(4 + i, 6) = 1 Then

The indentation is good, but you seem to arbitrarily use new lines. Sometimes no empty line, sometimes one, sometimes two. It makes for hard to read code. Hard to read code is hard to maintain code. Hard to maintain code will have bugs.

The other thing going on here is the "right aligned" comments. Which, as you can see, aren't right aligned anymore the second you copy paste them anywhere else. I'm okay with end of line comments if they're short and helpful, but forget trying to align them. It's a waste of time. If they state the obvious, remove them. If they are more than a word or two, move them to the line before.

    For g = 1 To 10000
        If Not IsEmpty(Cells(4 + g, 4).value) Then
            NumberOfTags = NumberOfTags + 1
        End If
    Next g

    If Not ThisWorkbook.OPCServer1 Is Nothing Then
		Exit Sub
	End If
	
	'connect to the OPC Server
    Set ThisWorkbook.OPCServer1 = New OPCServer
    Call ThisWorkbook.OPCServer1.Connect(Cells(4, 2))   
	
    Set OPCGroup1 = ThisWorkbook.OPCServer1.OPCGroups.Add(GrpName) 

    'add the 6 items
    ReDim MyOPCItems(NumberOfTags)
	
    For i = 1 To NumberOfTags
        On Error GoTo Error_TagNotFound

##Scoping (kind of)

This is not a scoping issue with your code per se, but still a readability issue.

>     'Dim OPCServer1 As OPCServer
>     Dim WithEvents OPCGroup1 As OPCGroup
>     Dim MyOPCItems() As OPCItem
>     Dim NumberOfTags As Integer
>     Dim ReadInterval As Double

Dont' use `Dim` at the module level. Use `Private`. Yes, they both do the same thing, but using `Private` is a visual indication that these variables belong to the module scope. 

You should also explicitly declare the scope of your Subs & Functions. Everything is public by default, but I find it much nicer to say so. (Particularly since in VB.Net, everything is private by default. It makes for less confusion when switching between the languages.)

So, 

    Sub OPC_Connect()

Should be 

    Public Sub OPC_Connect()

Or 

    Private Sub OPC_Connect()

Probably the latter. As a rule of thumb, declare everything private until you prove a need to call it from outside the current module or class.

>     Dim i As Integer
>     Dim g As Integer
>     Dim h As Integer

Here you're declaring 3 different counter variables and using them once. Don't do this. It's clutter. Just re-use the `i` variable as a counter for each loop. The eye will instantly recognize it as a loop counter. Or even better, extract some methods so you don't have to stress about re-using variables. (I'll come back to that in a bit.)

That was also more of a readability issue, but this "scoping" issue is a seriously dangerous thing. I can not think of a single situation where I ever should have legitimately called just plain old `Cells`.

>     'fetch the values from the cells
>     For i = 1 To 6
>      Values(i) = Cells(8 + i, 3)
>      If Values(i) = "" Then
>       Values(i) = 0
>      End If
>     Next i

`Cells` the way you have used it here is equivalent to calling `ActiveSheet.Cells`. Almost never should we be working with the active sheet because the user can change it on us while the code is executing. [Avoid activate and select](https://stackoverflow.com/q/10714251/3198973) by explicitly setting a variable to the range you want to work with.

The same goes for `Sheets`.  This code

    If Sheets("Setup").audible_alarms_enable_ChkBx.value = True Then

Is equivalent to calling `ThisWorkbook.Sheets("Setup")`. If the user clicks on another workbook, your code is going to blow up because it can't find the `"Setup"` worksheet.

##Naming

Names should not have underscores in them unless they are an event procedure. The underscore holds this special meaning in VBA. If it's not an event procedure, just don't do it. It's confusing. Particularly when you're making use of `WithEvents`. (Kudos for that by the way. I don't see many programmers take advantage of that.)

##Remove Dead Code

Dead code is clutter. There is dead commented out code all over the place. Burn it with fire. If you're worried about needing that code again, I highly recommend that you start using source control. I wrote a blog about [using Git with VBA](https://christopherjmcclellan.wordpress.com/2014/10/10/vba-and-git/) that should get you started.

#Single Responsiblity

You've done an okay job of using subroutines, but you could do better. Remember those alphabetic counters? Extract them into well named subroutines and call them from your "main" routine.

    Private Sub NotSureWhatThisActuallyDoes()
        For h = 1 To 7
            Module1.SavedThisTime(h) = False
        Next h
    End Sub

A corner stone of "bullet proof" code is that you can easily grok an entire routine *at one time*. It's often referred to as the single responsibility principle. Each routine or function should have one *and only one* job. Another way of thinking about it is the "Single Screen Principle". I should never have to scroll to see more of a routine. If I have to scroll, then the routine is most certainly breaking the SRP.

Sidenote: Speaking of "calling" routines, don't actually use the `Call` keyword. It's archaic and only available for back ward compatibility reasons.

Here is a really good example of an opportunity to extract a method. The comment is a dead give away (and a good start on a good name).

>     'set up which items to be read
>     For i = 1 To NumberOfTags
>         ServerHandles(i) = MyOPCItems(i).ServerHandle
>     Next i

#Other things

>        'free the memory
>        Erase Values()
>        Erase Errors()
>     
>     Exit Sub

It's nice that you're cleaning up after yourself, but I seriously don't understand why you're doing this. These arrays are locally scoped. Once they go out of scope the reference count will decrement to zero and the memory will be reclaimed.